<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js">jslet.ui.htmlclass.TABLECLASS = {
	currentrow: 'jl-tbl-current',
	scrollBarWidth: 16,
	selectColWidth: 30,
	hoverrow: 'jl-tbl-row-hover',
};

if(!jslet.ui.table) {
	jslet.ui.table = {};
}

<span id='jslet-ui-table-TableColumn'>/**
</span> * @class
 * 
 * Table column.
 */
jslet.ui.table.TableColumn = function() {
	var Z = this;
	
<span id='jslet-ui-table-TableColumn-property-field'>	/**
</span>	 * @property {String} field Field name.
	 */
	Z.field = null;
	
<span id='jslet-ui-table-TableColumn-property-label'>	/**
</span>	 * @property {String} label Column header label.
	 */
	Z.label = null;
	
<span id='jslet-ui-table-TableColumn-property-title'>	/**
</span>	 * @property {String} title Column header title.
	 */
	Z.title = null;
	
<span id='jslet-ui-table-TableColumn-property-prefix'>	/**
</span>	 * @property {jslet.ui.FieldControlAddon[]} prefix The prefix part of the field when editing.
	 */
	Z.prefix = null;
	
<span id='jslet-ui-table-TableColumn-property-suffix'>	/**
</span>	 * @property {jslet.ui.FieldControlAddon[]} suffix The suffix part of the field when editing.
	 */
	Z.suffix = null;
	 
<span id='jslet-ui-table-TableColumn-property-width'>	/**
</span>	 * @property {Integer} width Column display width.
	 */
	Z.width = null;   //Integer, column display width
	
<span id='jslet-ui-table-TableColumn-property-disableHeadSort'>	/**
</span>	 * @property {Boolean} disableHeadSort True - user sort this column by click column header, false - otherwise.
	 */
	Z.disableHeadSort = false;
	
<span id='jslet-ui-table-TableColumn-property-disableFilter'>	/**
</span>	 * @property {Boolean} disableFilter True - disable to show filter panel, false - otherwise.
	 */
	Z.disableFilter = false;

<span id='jslet-ui-table-TableColumn-property-mergeSame'>	/**
</span>	 * @property {Boolean} mergeSame True - if this column value of adjoined rows is same then merge these rows, false - otherwise.
	 */
	Z.mergeSame = false;
	
<span id='jslet-ui-table-TableColumn-property-noRefresh'>	/**
</span>	 * @property {Boolean} noRefresh True - do not refresh for customized column, false - otherwise.
	 */
	Z.noRefresh = false; //Boolean, true - do not refresh for customized column
	
<span id='jslet-ui-table-TableColumn-property-visible'>	/**
</span>	 * @property {Boolean} visible True - column is visible, false - otherwise.
	 */
	Z.visible = true;

<span id='jslet-ui-table-TableColumn-property-cellRender'>	/**
</span>	 * @property {jslet.ui.table.CellRender} cellRender Column cell render for customized column.
	 */
	Z.cellRender = null;  

	Z.colNum = null;  //Integer, column number
	Z.displayOrder = null; //Integer, display order
	Z.colSpan = null; //Integer, column span
};

/*
 * Sub group, use this class to implement complex requirement in one table row, like master-detail style row
 */
jslet.ui.table.TableSubgroup = function() {
var Z = this;
	Z.hasExpander = true; //Boolean, true - will add a new column automatically, click this column will expand or collapse subgroup panel
	Z.template = null;//String, html template 
	Z.height = 0; //Integer, subgroup panel height
};

<span id='jslet-ui-table-TableHead'>/**
</span> * @class
 * 
 * Table column header, use this class to implement hierarchical header
 */
jslet.ui.table.TableHead = function() {
	var Z = this;
	
<span id='jslet-ui-table-TableHead-property-label'>	/**
</span>	 * @property {String} label Head label.
	 */
	Z.label = null;
	
<span id='jslet-ui-table-TableHead-property-title'>	/**
</span>	 * @property {String} title Head title.
	 */
	Z.title = null;
	
<span id='jslet-ui-table-TableHead-property-id'>	/**
</span>	 * @property {String} id Head id.
	 */
	Z.id = null;
	
<span id='jslet-ui-table-TableHead-property-disableHeadSort'>	/**
</span>	 * @property {Boolean} disableHeadSort True - user sort this column by click column header, false - otherwise.
	 */
	Z.disableHeadSort = false;
	
<span id='jslet-ui-table-TableHead-property-disableFilter'>	/**
</span>	 * @property {Boolean} disableFilter True - disable to show filter panel, false - otherwise.
	 */
	Z.disableFilter = false;

<span id='jslet-ui-table-TableHead-property-subHeads'>	/**
</span>	 * @property {jslet.ui.table.TableHead[]} subHeads Sub heads.
	 */
	Z.subHeads = null;

	Z.rowSpan = 0;
	Z.colSpan = 0;
	
};

<span id='jslet-ui-AbstractDBTable'>/**
</span> * @class
 * @extend jslet.ui.DBControl
 */
jslet.ui.AbstractDBTable = jslet.Class.create(jslet.ui.DBControl, {
<span id='jslet-ui-AbstractDBTable-method-initialize'>	/**
</span>	 * @protected
	 * @override
	 */
	initialize: function($super, el, params) {
		var Z = this;
		
		Z.allProperties = 'styleClass,dataset,fixedRows,fixedCols,hasSeqCol,hasSelectCol,reverseSeqCol,seqColHeader,noborder,readOnly,editable,hideHead,' + 
			'disableHeadSort,onlySpecifiedCol,disableMouseWheel,selectBy,rowHeight,footerRowHeight,onRowClick,onRowDblClick,onSelect,onSelectAll,' + 
			'beforeSelect,beforeSelectAll,afterSelect,afterSelectAll,onFillRow,onFillCell,treeField,columns,subgroup,aggregated,' + 
			'autoClearSelection,onCellClick,defaultCellRender,hasFindDialog,hasFilterDialog,autoStretch,minVisibleRows,maxVisibleRows' +
			'onAppendRecord,onDeleteRecord,editableFields,readOnlyFields,enableErrorFinding';
		
		Z._fixedRows = 0;

		Z._fixedCols = 0;

		Z._hasSeqCol = true;
		
		Z._reverseSeqCol = false;
	
		Z._seqColHeader = null;

		Z._hasSelectCol = false;
		
		Z._noborder = false;
		
		Z._editable = false;

		Z._hideHead = false;
		
		Z._onlySpecifiedCol = false;
		
		Z._disableHeadSort = false;
		
		Z._disableMouseWheel = false;
		
		Z._aggregated = true;
		
		Z._autoClearSelection = true;
		
		Z._selectBy = null;

		Z._rowHeight = null;

		Z._headRowHeight = null;

		Z._footerRowHeight = null;
		
		Z._treeField = null;

		Z._columns = null;
		
		Z._editableFields = null;
		
		Z._readOnlyFields = null;
		
		Z._onRowClick = null;

		Z._onRowDblClick = null;
		
		Z._onCellClick = null;
		
		Z._onSelect = null;

		Z._onSelectAll = null;
		
		Z._beforeSelect = null;
		
		Z._afterSelect = null;
		
		Z._beforeSelectAll = null;
		
		Z._afterSelectAll = null;
		
		Z._onFillRow = null;
		
		Z._onFillCell = null;		

		Z._onAppendRecord = null;

		Z._onDeleteRecord = null;
		
		Z._defaultCellRender = null;

		Z._hasFindDialog = true;
		
		Z._hasFilterDialog = true;
		
		Z._enableErrorFinding = false;
		
		Z._repairHeight = 0;
		Z.contentHeight = 0;
		Z.subgroup = null;//jslet.ui.table.TableSubgroup
		
		Z._sysColumns = null;//all system column like sequence column, select column, sub-group column
		Z._isHoriOverflow = false;
		Z._oldHeight = null;
		
		Z._currRow = null;
		Z._currColNum = 0;
		Z._editingField = null;
		Z._editorTabIndex = 1;
		Z._rowHeightChanged = false;
		Z._autoStretch = false;
		Z._minVisibleRows = 1;
		Z._maxVisibleRows = 0;
		Z._oldVisibleRows = -1;
		
		Z._cellEditor = null;
		
		$super(el, params);
	},
	
<span id='jslet-ui-AbstractDBTable-property-fixedRows'>	/**
</span>	 * @property
	 * 
	 * Set or get fixed rows.
	 * 
	 * @param {Integer | undefined} rows Fixed rows.
	 * 
	 * @return {this | Integer}
	 */
	fixedRows: function(rows) {
		if(rows === undefined) {
			return this._fixedRows;
		}
		jslet.Checker.test('DBTable.fixedRows', rows).isNumber();
		this._fixedRows = parseInt(rows);
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-fixedCols'>	/**
</span>	 * @property
	 * 
	 * Set or get Fixed columns.
	 * 
	 * @param {Integer | undefined} cols Fixed columns.
	 * 
	 * @return {this | Integer}
	 */
	fixedCols: function(cols) {
		if(cols === undefined) {
			return this._fixedCols;
		}
		jslet.Checker.test('DBTable.fixedCols', cols).isNumber();
		this._fixedCols = parseInt(cols);
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-rowHeight'>	/**
</span>	 * @property
	 * 
	 * Set or get row height of table row.
	 * 
	 * @param {Integer | undefined} rowHeight Table row height.
	 * 
	 * @return {this | Integer}
	 */
	rowHeight: function(rowHeight) {
		var Z = this;
		if(rowHeight === undefined) {
			if(Z._rowHeight === null) {
				var clsName = Z._editable? 'jl-tbl-editing-row': 'jl-tbl-row';
				Z._rowHeight = parseInt(jslet.ui.getCssValue(clsName, 'height')) || 25;
			}
			return Z._rowHeight;
		}
		jslet.Checker.test('DBTable.rowHeight', rowHeight).isGTZero();
		Z._rowHeight = parseInt(rowHeight);
		Z._rowHeightChanged = true;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-headRowHeight'>	/**
</span>	 * @property
	 * 
	 * Set or get row height of table header.
	 * 
	 * @param {Integer | undefined} headRowHeight Table header row height.
	 * 
	 * @return {this | Integer}
	 */
	headRowHeight: function(headRowHeight) {
		if(headRowHeight === undefined) {
			if(this._headRowHeight === null) {
				this._headRowHeight = parseInt(jslet.ui.getCssValue('jl-tbl-header-row', 'height')) || 25;
			}
			return this._headRowHeight;
		}
		jslet.Checker.test('DBTable.headRowHeight', headRowHeight).isGTZero();
		this._headRowHeight = parseInt(headRowHeight);
		return this;
	},
<span id='jslet-ui-AbstractDBTable-property-footerRowHeight'>	/**
</span>	 * @property
	 * 
	 * Set or get row height of table footer.
	 * 
	 * @param {Integer | undefined} footerRowHeight Table footer row height.
	 * 
	 * @return {this | Integer}
	 */
	footerRowHeight: function(footerRowHeight) {
		if(footerRowHeight === undefined) {
			if(this._footerRowHeight === null) {
				this._footerRowHeight = parseInt(jslet.ui.getCssValue('jl-tbl-footer-row', 'height')) || 20;
			}
			return this._footerRowHeight;
		}
		jslet.Checker.test('DBTable.footerRowHeight', footerRowHeight).isGTZero();
		this._footerRowHeight = parseInt(footerRowHeight);
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-hasSeqCol'>	/**
</span>	 * @property
	 * 
	 * Identify whether there is sequence column in DBTable.
	 * 
	 * @param {Boolean | undefined} hasSeqCol True(default) - exists sequence column, false - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	hasSeqCol: function(hasSeqCol) {
		if(hasSeqCol === undefined) {
			return this._hasSeqCol;
		}
		this._hasSeqCol = hasSeqCol ? true: false;
		return this;
	},

<span id='jslet-ui-AbstractDBTable-property-reverseSeqCol'>	/**
</span>	 * @property
	 * 
	 * Identify whether the sequence number is reverse.
	 * 
	 * @param {Boolean | undefined} reverseSeqCol True - the sequence number is reverse, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	reverseSeqCol: function(reverseSeqCol) {
		if(reverseSeqCol === undefined) {
			return this._reverseSeqCol;
		}
		this._reverseSeqCol = reverseSeqCol ? true: false;
		return this;
	},
		
<span id='jslet-ui-AbstractDBTable-property-seqColHeader'>	/**
</span>	 * @property
	 * 
	 * Set or get sequence column header.
	 * 
	 * @param {String | undefined} seqColHeader Sequence column header.
	 * 
	 * @return {this | String}
	 */
	seqColHeader: function(seqColHeader) {
		if(seqColHeader === undefined) {
			return this._seqColHeader;
		}
		this._seqColHeader = seqColHeader;
		return this;
	},
		
<span id='jslet-ui-AbstractDBTable-property-hasSelectCol'>	/**
</span>	 * @property
	 * 
	 * Identify whether there is &quot;select&quot; column in DBTable.
	 * 
	 * @param {Boolean | undefined} hasSelectCol True(default) - has &quot;select&quot; column, false - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	hasSelectCol: function(hasSelectCol) {
		if(hasSelectCol === undefined) {
			return this._hasSelectCol;
		}
		this._hasSelectCol = hasSelectCol ? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-noborder'>	/**
</span>	 * @property
	 * 
	 * Identify the table has border or not.
	 * 
	 * @param {Boolean | undefined} noborder True - the table without border, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	noborder: function(noborder) {
		if(noborder === undefined) {
			return this._noborder;
		}
		this._noborder = noborder ? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-method-readOnly'>	/**
</span>	 * @deprecated
	 * Use editable instead.
	 * 
	 * Identify the table is read only or not.
	 * 
	 * @param {Boolean | undefined} readOnly True(default) - the table is read only, false - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	readOnly: function(readOnly) {
		var Z = this;
		if(readOnly === undefined) {
			return !Z._editable;
		}
		Z.editable(!readOnly);
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-editable'>	/**
</span>	 * @property
	 * 
	 * Identify the table is editable or not.
	 * 
	 * @param {Boolean | undefined} editable True(default) - the table is editable, false - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	editable: function(editable) {
		var Z = this;
		if(editable === undefined) {
			return Z._editable;
		}
		Z._editable = editable ? true: false;
		if(Z._editable &amp;&amp; !Z._rowHeightChanged) {
			Z._rowHeight = null;
		}
		return this;
	},
	
	
<span id='jslet-ui-AbstractDBTable-property-hideHead'>	/**
</span>	 * @property
	 * 
	 * Identify the table header is hidden or not.
	 * 
	 * @param {Boolean | undefined} hideHead True - the table header is hidden, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	hideHead: function(hideHead) {
		if(hideHead === undefined) {
			return this._hideHead;
		}
		this._hideHead = hideHead ? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-aggregated'>	/**
</span>	 * @property
	 * 
	 * Identify the table has aggregated row or not.
	 * 
	 * @param {Boolean | undefined} aggregated True - the table has aggregated row, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	aggregated: function(aggregated) {
		if(aggregated === undefined) {
			return this._aggregated;
		}
		this._aggregated = aggregated ? true: false;
		return this;
	},

<span id='jslet-ui-AbstractDBTable-property-autoClearSelection'>	/**
</span>	 * @property
	 * 
	 * Identify whether automatically clear selection when selecting table cells.
	 * 
	 * @param {Boolean | undefined} autoClearSelection True(default) - automatically clear selection, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	autoClearSelection: function(autoClearSelection) {
		if(autoClearSelection === undefined) {
			return this._autoClearSelection;
		}
		this._autoClearSelection = autoClearSelection ? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-disableHeadSort'>	/**
</span>	 * @property
	 * 
	 * Identify disable table head sorting or not.
	 * 
	 * @param {Boolean | undefined} disableHeadSort True - disable table header sorting, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	disableHeadSort: function(disableHeadSort) {
		if(disableHeadSort === undefined) {
			return this._disableHeadSort;
		}
		this._disableHeadSort = disableHeadSort ? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-disableMouseWheel'>	/**
</span>	 * @property
	 * 
	 * Disable to use mouse wheel to scroll rows or not.
	 * 
	 * @param {Boolean | undefined} disableMouseWheel True - disable to use mouse wheel to scroll rows, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	disableMouseWheel: function(disableMouseWheel) {
		if(disableMouseWheel === undefined) {
			return this._disableMouseWheel;
		}
		this._disableMouseWheel = disableMouseWheel ? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-onlySpecifiedCol'>	/**
</span>	 * @property
	 * 
	 * Identify whether showing the specified columns or not.
	 * 
	 * @param {Boolean | undefined} onlySpecifiedCol True - only showing the specified columns, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	onlySpecifiedCol: function(onlySpecifiedCol) {
		if(onlySpecifiedCol === undefined) {
			return this._onlySpecifiedCol;
		}
		this._onlySpecifiedCol = onlySpecifiedCol ? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-selectBy'>	/**
</span>	 * @property
	 * 
	 * Specified field names for selecting group records, multiple field names are separated with ','.&lt;br /&gt;
	 * See jslet.data.Dataset.select(selected, selectBy). Example:
	 * 
	 *     @example
	 *     tblObj.selectBy('code,gender');
	 * 
	 * @param {String | undefined} selectBy group selecting field names.
	 * 
	 * @return {this | String}
	 */
	selectBy: function(selectBy) {
		if(selectBy === undefined) {
			return this._selectBy;
		}
		jslet.Checker.test('DBTable.selectBy', selectBy).isString();
		this._selectBy = selectBy;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-treeField'>	/**
</span>	 * @property
	 * 
	 * Display table as tree style. If this property is set, the dataset must be a tree style dataset, 
	 *  means dataset.parentField() and dataset.levelField() can not be empty.&lt;br /&gt;
	 * Only one field name allowed. Example:
	 * 
	 *     @example
	 *     tblObj.treeField('code');
	 * 
	 * @param {String | undefined} treeField the field name which will show as tree style.
	 * 
	 * @return {this | String}
	 */
	treeField: function(treeField) {
		if(treeField === undefined) {
			return this._treeField;
		}
		jslet.Checker.test('DBTable.treeField', treeField).isString();
		this._treeField = treeField;
		return this;
	},

<span id='jslet-ui-AbstractDBTable-property-defaultCellRender'>	/**
</span>	 * @property
	 * 
	 * Default cell render, it must be a child class of jslet.ui.table.CellRender. Example:
	 * 
	 *     @example
	 * 	   var cellRender = jslet.Class.create(jslet.ui.table.CellRender, {
	 *	     createHeader: function(cellPanel, colCfg) { },
	 *	     createCell: function(cellPanel, colCfg) { },
	 *	     refreshCell: function(cellPanel, colCfg, recNo) { }
	 *     });
	 * 
	 * @param {jslet.ui.table.CellRender} defaultCellRender Default cell render.
	 * 
	 * @return {this | jslet.ui.table.CellRender}
	 * 
	 */
	defaultCellRender: function(defaultCellRender) {
		if(defaultCellRender === undefined) {
			return this._defaultCellRender;
		}
		jslet.Checker.test('DBTable.defaultCellRender', defaultCellRender).isObject();
		
		this._defaultCellRender = defaultCellRender;
		return this;
	},
	
	currColNum: function(currColNum) {
		var Z = this;
		if(currColNum === undefined) {
			return Z._currColNum;
		}
		var oldColNum = Z._currColNum;
		Z._currColNum = currColNum;
		if(oldColNum !== currColNum) {
			Z._adjustCurrentCellPos(oldColNum &gt; currColNum);
			Z._showCurrentCell();
		}
		if(Z._findDialog) {
			var colCfg = Z.innerColumns[currColNum];
			if(colCfg.field) {
				Z._findDialog.findingField(colCfg.field);
			}
		}
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onRowClick'>	/**
</span>	 * @event
	 * 
	 * Fired when table row clicked. Example:
	 * 
	 *     @example
	 *     tblObj.onRowClick(function(otr) {});
	 *     
	 * @param {Function | undefined} onRowClick Table row clicked event handler.
	 * @param {HtmlElement} onRowClick.trElement Table row element.
	 * 
	 * @return {this | Function}
	 */
	onRowClick: function(onRowClick) {
		if(onRowClick === undefined) {
			return this._onRowClick;
		}
		jslet.Checker.test('DBTable.onRowClick', onRowClick).isFunction();
		this._onRowClick = onRowClick;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onRowDblClick'>	/**
</span>	 * @event
	 * 
	 * Fired when table row double clicked. Example:
	 * 
	 *     @example
	 *     tblObj.onRowDblClick(function(otr) {});
	 *     
	 * @param {Function | undefined} onRowDblClick Table row double clicked event handler.
	 * @param {HtmlElement} onRowDblClick.trElement Table row element.
	 * 
	 * @return {this | Function}
	 */
	onRowDblClick: function(onRowDblClick) {
		if(onRowDblClick === undefined) {
			return this._onRowDblClick;
		}
		jslet.Checker.test('DBTable.onRowDblClick', onRowDblClick).isFunction();
		this._onRowDblClick = onRowDblClick;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onCellClick'>	/**
</span>	 * @event
	 * 
	 * Fired when table cell clicked. Example:
	 * 
	 *     @example
	 *     tblObj.onCellClick(function(otd) {});
	 *     
	 * @param {Function | undefined} onCellClick Table cell clicked event handler.
	 * @param {HtmlElement} onCellClick.tdElement Table cell element.
	 * 
	 * @return {this | Function}
	 */
	onCellClick: function(onCellClick) {
		if(onCellClick === undefined) {
			return this._onCellClick;
		}
		jslet.Checker.test('DBTable.onCellClick', onCellClick).isFunction();
		this._onCellClick = onCellClick;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onSelect'>	/**
</span>	 * @event
	 * 
	 * Fired when table row is selected(select column is checked). Example:
	 * 
	 *     @example
	 *     tblObj.onSelect(function(checked) {});
	 *     
	 * @param {Function | undefined} onSelect Table row selected event handler.
	 * @param {Boolean} onSelect.checked True - table row is selected, false - otherwise.
	 * 
	 * @return {this | Function}
	 */
	onSelect: function(onSelect) {
		if(onSelect === undefined) {
			return this._onSelect;
		}
		jslet.Checker.test('DBTable.onSelect', onSelect).isFunction();
		this._onSelect = onSelect;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onSelectAll'>	/**
</span>	 * @event
	 * 
	 * Fired when all table rows are selected. Example:
	 * 
	 *     @example
	 *     tblObj.onSelectAll(function(dataset, checked) {});
	 *     
	 * @param {Function | undefined} onSelectAll All Table row selected event handler.
	 * @param {jslet.data.Dataset} onSelectAll.dataset The current dataset object.
	 * @param {Boolean} onSelectAll.checked True - all rows selected, false - otherwise.
	 * 
	 * @return {this | Function}
	 */
	onSelectAll: function(onSelectAll) {
		if(onSelectAll === undefined) {
			return this._onSelectAll;
		}
		jslet.Checker.test('DBTable.onSelectAll', onSelectAll).isFunction();
		this._onSelectAll = onSelectAll;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-beforeSelect'>	/**
</span>	 * @event
	 * 
	 * Fired before table row is selected(select column is checked). Example:
	 * 
	 *     @example
	 *     tblObj.beforeSelect(function(selected) {
	 *       return true;
	 *     });
	 *     
	 * @param {Function | undefined} beforeSelect Table row selecting event handler.
	 * @param {Boolean} beforeSelect.selected True - table row is selected, false - otherwise.
	 * @param {Boolean} beforeSelect.return True - table row is allowed to select, false - otherwise.
	 * 
	 * @return {this | Function}
	 */
	beforeSelect: function(beforeSelect) {
		if(beforeSelect === undefined) {
			return this._beforeSelect;
		}
		jslet.Checker.test('DBTable.beforeSelect', beforeSelect).isFunction();
		this._beforeSelect = beforeSelect;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-afterSelect'>	/**
</span>	 * @event
	 * 
	 * Fired after table row is selected(select column is checked). Example:
	 * 
	 *     @example
	 *     tblObj.afterSelect(function() {});
	 *     
	 * @param {Function | undefined} afterSelect Table row after selected event handler.
	 * 
	 * @return {this | Function}
	 */
	afterSelect: function(afterSelect) {
		if(afterSelect === undefined) {
			return this._afterSelect;
		}
		jslet.Checker.test('DBTable.afterSelect', afterSelect).isFunction();
		this._afterSelect = afterSelect;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-beforeSelectAll'>	/**
</span>	 * @event
	 * 
	 * Fired before all table rows are selected(select column is checked). Example:
	 * 
	 *     @example
	 *     tblObj.beforeSelectAll(function(selected) {
	 *       return true;
	 *     });
	 *     
	 * @param {Function | undefined} beforeSelectAll All table rows are being selected event handler.
	 * @param {Boolean} beforeSelectAll.selected True - all table rows are selected, false - otherwise.
	 * @param {Boolean} beforeSelectAll.return True - all table rows are allowed to select, false - otherwise.
	 * 
	 * @return {this | Function}
	 */
	beforeSelectAll: function(beforeSelectAll) {
		if(beforeSelectAll === undefined) {
			return this._beforeSelectAll;
		}
		jslet.Checker.test('DBTable.beforeSelectAll', beforeSelectAll).isFunction();
		this._beforeSelectAll = beforeSelectAll;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-afterSelectAll'>	/**
</span>	 * @event
	 * 
	 * Fired after all table rows are selected(select column is checked). Example:
	 * 
	 *     @example
	 *     tblObj.afterSelectAll(function() {});
	 *     
	 * @param {Function | undefined} afterSelectAll Event handler for &quot;after all table rows are selected&quot;.
	 * 
	 * @return {this | Function}
	 */
	afterSelectAll: function(afterSelectAll) {
		if(afterSelectAll === undefined) {
			return this._afterSelectAll;
		}
		jslet.Checker.test('DBTable.afterSelectAll', afterSelectAll).isFunction();
		this._afterSelectAll = afterSelectAll;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onFillRow'>	/**
</span>	 * @event
	 * 
	 * Fired when fill row, user can use this to customize each row style like background color, font color. Example:
	 * 
	 *     @example
	 *     tblObj.onFillRow(function(trEle, dsObj) {});
	 *   
	 * @param {Function | undefined} onFillRow Table row filled event handler.
	 * @param {HtmlElement} onFillRow.trElement Table row element.
	 * @param {jslet.data.Dataset} onFillRow.dataset Current dataset object.
	 * 
	 * @return {this | Function}
	 */
	onFillRow: function(onFillRow) {
		if(onFillRow === undefined) {
			return this._onFillRow;
		}
		jslet.Checker.test('DBTable.onFillRow', onFillRow).isFunction();
		this._onFillRow = onFillRow;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onFillCell'>	/**
</span>	 * @event
	 * 
	 * Fired when fill cell, user can use this to customize each cell style like background color, font color. Example:
	 * 
	 *     @example
	 *     tblObj.onFillRow(function(trEle, dsObj, fldName) {});
	 * 
	 * @param {Function | undefined} onFillCell Table cell filled event handler.
	 * @param {HtmlElement} onFillCell.tdElement Table cell element.
	 * @param {jslet.data.Dataset} onFillCell.dataset Current dataset object.
	 * @param {String} onFillCell.fieldName Field name.
	 * 
	 * @return {this | Function}
	 */
	onFillCell: function(onFillCell) {
		if(onFillCell === undefined) {
			return this._onFillCell;
		}
		jslet.Checker.test('DBTable.onFillCell', onFillCell).isFunction();
		this._onFillCell = onFillCell;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onAppendRecord'>	/**
</span>	 * @event
	 * 
	 * Fired when user click the '+' button of the 'Edit' column. Example:
	 * 
	 *     @example
	 *     tblObj.onAppendRecord(function() {
	 *        this.dataset().appendRecord();
	 *     });
	 * 
	 * @param {Function | undefined} onAppendRecord '+’ button event handler.
	 * 
	 * @return {this | Function}
	 */
	onAppendRecord: function(onAppendRecord) {
		if(onAppendRecord === undefined) {
			return this._onAppendRecord;
		}
		jslet.Checker.test('DBTable.onAppendRecord', onAppendRecord).isFunction();
		this._onAppendRecord = onAppendRecord;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-event-onDeleteRecord'>	/**
</span>	 * @event
	 * 
	 * Fired when user click the '-' button of the 'Edit' column. Example:
	 * 
	 *     @example
	 *     tblObj.onDeleteRecord(function() {
	 *        this.dataset().deleteRecord();
	 *     });
	 * 
	 * @param {Function | undefined} onDeleteRecord '-’ button event handler.
	 * 
	 * @return {this | Function}
	 */
	onDeleteRecord: function(onDeleteRecord) {
		if(onDeleteRecord === undefined) {
			return this._onDeleteRecord;
		}
		jslet.Checker.test('DBTable.onDeleteRecord', onDeleteRecord).isFunction();
		this._onDeleteRecord = onDeleteRecord;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-hasFindDialog'>	/**
</span>	 * @property
	 * 
	 * Identify the table has finding dialog or not.
	 * 
	 * @param {Boolean | undefined} hasFindDialog true(default) - show finding dialog when press 'Ctrl + F', false - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	hasFindDialog: function(hasFindDialog) {
		var Z = this;
		if(hasFindDialog === undefined) {
			return Z._hasFindDialog;
		}
		Z._hasFindDialog = hasFindDialog? true: false;
		return this;
	},

<span id='jslet-ui-AbstractDBTable-property-hasFilterDialog'>	/**
</span>	 * @property
	 * 
	 * Identify the table has filter dialog or not.
	 * 
	 * @param {Boolean | undefined} hasFilterDialog true(default) - show filter dialog when creating table, false - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	hasFilterDialog: function(hasFilterDialog) {
		var Z = this;
		if(hasFilterDialog === undefined) {
			return Z._hasFilterDialog;
		}
		Z._hasFilterDialog = hasFilterDialog? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-enableErrorFinding'>	/**
</span>	 * @property
	 * 
	 * Identify whether it's showing the 'Error find' menu item in the context menu.
	 * 
	 * @param {Boolean | undefined} enableErrorFinding True - show error find menu item in the context menu, false - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	enableErrorFinding: function(enableErrorFinding) {
		if(enableErrorFinding === undefined) {
			return this._enableErrorFinding;
		}
		this._enableErrorFinding = enableErrorFinding ? true: false;
		return this;
	},
		
<span id='jslet-ui-AbstractDBTable-property-autoStretch'>	/**
</span>	 * @property
	 * 
	 * Identify whether the table height is stretched automatically according to the dataset's records.
	 * 
	 * @param {Boolean | undefined} autoStretch True - The table height is stretched automatically, false(default) - otherwise.
	 * 
	 * @return {this | Boolean}
	 */
	autoStretch: function(autoStretch) {
		var Z = this;
		if(autoStretch === undefined) {
			return Z._autoStretch;
		}
		Z._autoStretch = autoStretch? true: false;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-minVisibleRows'>	/**
</span>	 * @property
	 * 
	 * Set or get the minimum visible rows of the table.
	 * 
	 * @param {Integer | undefined} minVisibleRows Minimum visible rows.
	 * 
	 * @return {this | Integer}
	 */
	minVisibleRows: function(minVisibleRows) {
		if(minVisibleRows === undefined) {
			return this._minVisibleRows;
		}
		jslet.Checker.test('DBTable.minVisibleRows', minVisibleRows).isGTZero();
		this._minVisibleRows = parseInt(minVisibleRows);
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-maxVisibleRows'>	/**
</span>	 * @property
	 * 
	 * Set or get the minimum visible rows of the table.
	 * 
	 * @param {Integer | undefined} maxVisibleRows Minimum visible rows.
	 * 
	 * @return {this | Integer}
	 */
	maxVisibleRows: function(maxVisibleRows) {
		if(maxVisibleRows === undefined) {
			return this._maxVisibleRows;
		}
		jslet.Checker.test('DBTable.maxVisibleRows', maxVisibleRows).isNumber();
		this._maxVisibleRows = parseInt(maxVisibleRows);
		return this;
	},
		
	cellEditor: function() {
		var Z = this;
		if(!Z._editable) {
			return null;
		}
		if(!Z._cellEditor) {
			Z._cellEditor = new jslet.ui.table.TableCellEditor(Z);
		}
		return Z._cellEditor;
	},

<span id='jslet-ui-AbstractDBTable-property-columns'>	/**
</span>	 * @property
	 * 
	 * Table columns configurations, array of jslet.ui.table.TableColumn.
	 * 
	 * @param {jslet.ui.table.TableColumn[] | undefined} columns Table columns configurations.
	 * 
	 * @return {this | jslet.ui.table.TableColumn[]}
	 */
	columns: function(columns) {
		if(columns === undefined) {
			return this._columns;
		}
		jslet.Checker.test('DBTable.columns', columns).isArray();
		var colObj;
		for(var i = 0, len = columns.length; i &lt; len; i++) {
			colObj = columns[i];
			jslet.Checker.test('DBTable.Column.field', colObj.field).isString();
			jslet.Checker.test('DBTable.Column.label', colObj.label).isString();
			jslet.Checker.test('DBTable.Column.colNum', colObj.colNum).isGTEZero();
			jslet.Checker.test('DBTable.Column.displayOrder', colObj.displayOrder).isNumber();
			jslet.Checker.test('DBTable.Column.width', colObj.width).isGTZero();
			jslet.Checker.test('DBTable.Column.colSpan', colObj.colSpan).isGTZero();
			jslet.Checker.test('DBTable.Column.prefix', colObj.prefix).isArray();
			jslet.Checker.test('DBTable.Column.suffix', colObj.suffix).isArray();
			if(!colObj.field) {
				colObj.disableHeadSort = true;
				colObj.disableFilter = true;
			} else {
				colObj.disableHeadSort = colObj.disableHeadSort ? true: false;
				colObj.disableFilter = colObj.disableFilter ? true: false;
			}
			colObj.mergeSame = colObj.mergeSame ? true: false;
			colObj.noRefresh = colObj.noRefresh ? true: false;
			jslet.Checker.test('DBTable.Column.cellRender', colObj.cellRender).isObject();
		}
		this._columns = columns;
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-property-editableFields'>	/**
</span>	 * @property
	 * 
	 * Set or get editable fields when DBTable is in editable. It is contrast to 'readOnlyFields'.&lt;br /&gt;
	 * If the read only columns' count is greater than editable fields, use 'readOnlyFields' instead. &lt;br /&gt;
	 * If 'editableFields' and 'readOnlyFields' are not specified, editable fields comes from jslet.data.Field object.
	 * 
	 * @param {String[] | String | undefined} editableFields Editable fields.
	 * 
	 * @return {this | String[]}
	 */
	editableFields: function(editableFields) {
		if(editableFields === undefined) {
			return this._editableFields;
		}
		if(jslet.isString(editableFields)) {
			editableFields = editableFields.split(',');
		} else {
			jslet.Checker.test('DBTable.editableFields', editableFields).isArray();
		}
		for(var i = 0, len = editableFields.length; i &lt; len; i++) {
			jslet.Checker.test('DBTable.editableFields#fieldName', editableFields[i]).required().isString();
			editableFields[i] = editableFields[i].trim();
		}
		this._editableFields = editableFields;
	},
	
<span id='jslet-ui-AbstractDBTable-property-readOnlyFields'>	/**
</span>	 * @property
	 * 
	 * Set or get read only fields when DBTable is in editable. It is contrast to 'editableFields'.&lt;br /&gt;
	 * If the editable columns' count is greater than read only fields, use 'editableFields' instead. &lt;br /&gt;
	 * If 'editableFields' and 'readOnlyFields' are not specified, editable fields comes from jslet.data.Field object.
	 * 
	 * @param {String[] | String | undefined} readOnlyFields Read only fields.
	 * 
	 * @return {this | String[]}
	 */
	readOnlyFields: function(readOnlyFields) {
		if(readOnlyFields === undefined) {
			return this._readOnlyFields;
		}
		if(jslet.isString(readOnlyFields)) {
			readOnlyFields = readOnlyFields.split(',');
		} else {
			jslet.Checker.test('DBTable.readOnlyFields', readOnlyFields).isArray();
		}
		for(var i = 0, len = readOnlyFields.length; i &lt; len; i++) {
			jslet.Checker.test('DBTable.readOnlyFields#fieldName', readOnlyFields[i]).required().isString();
			readOnlyFields[i] = readOnlyFields[i].trim();
		}
		this._readOnlyFields = readOnlyFields;
	},
	
<span id='jslet-ui-AbstractDBTable-method-gotoField'>	/**
</span>	 * Goto and show the specified cell by field name.
	 * 
	 * @param {String} fldName field name.
	 */
	gotoField: function(fldName) {
		jslet.Checker.test('DBTable.gotoField#fldName', fldName).required().isString();
		var colNum = this.getColNumByField(fldName);
		if(colNum &gt;= 0) {
			this.gotoColumn(colNum);
		}
	},
	
<span id='jslet-ui-AbstractDBTable-method-getColNumByField'>	/**
</span>	 * Get column number with field name.
	 * 
	 * @param {String} fldName Field name.
	 * 
	 * @return {Integer} Column number.
	 */
	getColNumByField: function(fldName) {
		var lastColNum = this.innerColumns.length - 1,
			colCfg;
		for(var i = 0; i &lt;= lastColNum; i++) {
			colCfg = this.innerColumns[i];
			if(colCfg.field == fldName) {
				return colCfg.colNum;
			}
		}
		return -1;
	},
	
<span id='jslet-ui-AbstractDBTable-method-getFieldByColNum'>	/**
</span>	 * Get field name with column number.
	 * 
	 * @param {Integer} colNum Column number.
	 * 
	 * @return {String} Field name.
	 */
	getFieldByColNum: function(colNum) {
		var lastColNum = this.innerColumns.length - 1,
			colCfg;
		for(var i = 0; i &lt;= lastColNum; i++) {
			colCfg = this.innerColumns[i];
			if(colCfg.colNum == colNum) {
				return colCfg.field;
			}
		}
		return null;
	},
	
<span id='jslet-ui-AbstractDBTable-method-gotoColumn'>	/**
</span>	 * Goto and show the specified cell by field name.
	 * 
	 * @param {String} fldName field name.
	 */
	gotoColumn: function(colNum) {
		jslet.Checker.test('DBTable.gotoColumn#colNum', colNum).required().isGTEZero();
		var lastColNum = this.innerColumns.length - 1;
		if(colNum &gt; lastColNum) {
			colNum = lastColNum;
		}
		this.currColNum(colNum);
	},
	
	editorTabIndex: function() {
		return this._editorTabIndex;
	},
	
<span id='jslet-ui-AbstractDBTable-method-isValidTemplateTag'>	/**
</span>	 * @protected
	 * @override
	 */
	isValidTemplateTag: function(el) {
		return el.tagName.toLowerCase() == 'div';
	},
	
	_calcTabIndex: function() {
		var Z = this,
			jqEl = jQuery(Z.el);
		if(Z._editable) {
			var masterFldObj = Z._dataset.getMasterFieldObject(),
				tbIdx = null;
			if(masterFldObj) {
				tbIdx = masterFldObj.tabIndex();
			}
			if(!tbIdx) {
				tbIdx = Z.el.tabIndex;
			}
			Z._editorTabIndex = tbIdx &amp;&amp; tbIdx &gt; 0? tbIdx: null;
		}
		Z.el.tabIndex = -1;
	},
	
<span id='jslet-ui-AbstractDBTable-method-bind'>	/**
</span>	 * @protected
	 * @override
	 */
	bind: function() {
		var Z = this;
		jslet.ui.resizeEventBus.subscribe(Z);
		
		jslet.ui.textMeasurer.setElement(Z.el);
		Z.charHeight = jslet.ui.textMeasurer.getHeight('M')+4;
		jslet.ui.textMeasurer.setElement();
		Z.charWidth = jslet.global.defaultCharWidth || 12;
		Z._widthStyleId = jslet.nextId();
		Z._initializeVm();
		Z._calcTabIndex();
		Z.renderAll();
		Z._bindEvents();
	}, // end bind
	
	_bindEvents: function() {
		var Z = this;
		var jqEl = jQuery(Z.el);
        var notFF = ((typeof Z.el.onmousewheel) == 'object'); //firefox or nonFirefox browser
        var wheelEvent = (notFF ? 'mousewheel' : 'DOMMouseScroll');
        jqEl.on(wheelEvent, function(event) {
        	if(Z._disableMouseWheel) {
        		return;
        	}
        	
            var originalEvent = event.originalEvent;
            var num = notFF ? originalEvent.wheelDelta / -120 : originalEvent.detail / 3;
			if(Z._editable &amp;&amp; Z._dataset.status() != jslet.data.DataSetStatus.BROWSE) {
				Z._dataset.confirm();
			}
            Z.listvm.setVisibleStartRow(Z.listvm.getVisibleStartRow() + num);
			var cellEditor = Z.cellEditor();
			if(cellEditor) {
				cellEditor.hideEditor();
			}
       		event.preventDefault();
        });

        jqEl.on('mousedown', function(event) {
        	if(event.shiftKey) {
	       		event.preventDefault();
	       		event.stopImmediatePropagation();
	       		return false;
        	}
        });
        
        jqEl.on('click', 'button.jl-tbl-filter', function(event) {
    		if (!Z._filterPanel) {
    			Z._filterPanel = new jslet.ui.table.DBTableFilterPanel(Z);
    		}
    		var btnEle = event.currentTarget,
    			jqFilterBtn = jQuery(btnEle),
    			tblPos = jQuery(Z.el).offset();
    		var r = jqFilterBtn.offset(), 
    			h = jqFilterBtn.outerHeight(), 
    			x = r.left - tblPos.left, 
    			y = r.top + h - tblPos.top;
    		if (jsletlocale.isRtl) {
    			x = x + jqFilterBtn.outerWidth();
    		}
    		var fldName = jqFilterBtn[0].getAttribute('jsletfilterfield');
    		Z._filterPanel.changeField(fldName);
    		Z._filterPanel.jqFilterBtn(jqFilterBtn);
    		Z._filterPanel.show(x, y, 0, h);
        	
       		event.preventDefault();
       		event.stopImmediatePropagation();
        });
        jqEl.on('dblclick', 'td.jl-tbl-cell', function(event) {
        	var otd = event.currentTarget;
        	Z._doDblCellClick(otd, event.offsetY);
        });
        
        jqEl.on('mousedown', 'td.jl-tbl-cell', function(event) {
        	var otd = event.currentTarget;
        	var offsetY = event.offsetY;
        	if(event.target != otd) {
        		offsetY = jQuery(event.target).offset().top - jQuery(otd).offset().top + event.offsetY;
        	}
        	Z._doCellClick(otd, offsetY, event);
        	
        	if(event.shiftKey || event.ctrlKey) {
	       		event.preventDefault();
	       		event.stopImmediatePropagation();
	       		return false;
        	}
        });

        jqEl.on('click', '.jl-tbltree-btn', function(event) {
        	function checkFn(node) {
				return node.tagName &amp;&amp; node.tagName.toLowerCase() == 'tr' &amp;&amp; (node.jsletrecno || node.jsletrecno === 0);
			}
			
			function callBackFn() {
				Z._fillData();
			}
			
			var otr = jslet.ui.findFirstParent(this, checkFn);
					
			event.stopImmediatePropagation();
			event.preventDefault();
			var recNo = otr.jsletrecno;
			Z._dataset.recno(recNo);
			if(Z._dataset.aborted()) {
				return false;
			}
			
			if (Z._dataset.expandedByRecno(recNo)) {
				Z.listvm.collapse(callBackFn);
			} else {
				Z.listvm.expand(callBackFn);
			}
			return false;
        });
        
		jqEl.on('keydown', function(event) {
			var keyCode = event.which,
				ctrlKey = event.ctrlKey,
				shiftKey = event.shiftKey,
				altKey = event.altKey;
			if(ctrlKey) {
				var flag = false;
				if(Z._hasFindDialog &amp;&amp; keyCode === jslet.ui.KeyCode.F) { //ctrl + f
					Z.showFindDialog();
					flag = true;
				}
				if(keyCode === jslet.ui.KeyCode.E &amp;&amp; (Z._enableErrorFinding || Z._editable)) { //ctrl + e
					Z.gotoNextError();
					flag = true;
				}
				if(keyCode === jslet.ui.KeyCode.A) { //ctrl + a
					Z.selectAllCells();
					flag = true;
				}
				if(keyCode === jslet.ui.KeyCode.HOME) { //ctrl + Home
					Z._dataset.first();
					flag = true;
				}
				if(keyCode === jslet.ui.KeyCode.END) { //ctrl + End
					Z._dataset.last();
					flag = true;
				}
				if(keyCode === jslet.ui.KeyCode.C) { //ctrl + c
					Z.copySelection(false);
					return;
				}
				if(flag) {
					event.preventDefault();
		       		event.stopImmediatePropagation();
					return false;
				}
			}
			var isTabKey = (keyCode === jslet.ui.KeyCode.TAB || keyCode === jslet.global.defaultFocusKeyCode);
			if(shiftKey &amp;&amp; isTabKey) { //Shift TAB Left
				if(!Z.tabPrior()) {
					return;
				}
			} else if(isTabKey) { //TAB Right
				if(!Z.tabNext()) {
					return;
				}
			} else if(keyCode === jslet.ui.KeyCode.LEFT) { //Arrow Left
				Z.movePriorCell(ctrlKey, shiftKey, altKey);
			} else if( keyCode === jslet.ui.KeyCode.RIGHT) { //Arrow Right
				Z.moveNextCell(ctrlKey, shiftKey, altKey);
			} else if (keyCode === jslet.ui.KeyCode.UP) {//KEY_UP
				Z._doBeforeSelect(ctrlKey, shiftKey, altKey);
				Z.listvm.priorRow();
				Z._processSelection(ctrlKey, shiftKey, altKey);
			} else if (keyCode === jslet.ui.KeyCode.DOWN) {//KEY_DOWN
				Z._doBeforeSelect(ctrlKey, shiftKey, altKey);
				Z.listvm.nextRow();
				Z._processSelection(ctrlKey, shiftKey, altKey);
			} else if (keyCode === jslet.ui.KeyCode.PAGEUP) {//KEY_PAGEUP
				Z.listvm.priorPage();
			} else if (keyCode === jslet.ui.KeyCode.PAGEDOWN) {//KEY_PAGEDOWN
				Z.listvm.nextPage();
			} else {
				return;
			}
			if(keyCode === jslet.ui.KeyCode.ENTER &amp;&amp; !Z._editable) {
				return;
			}
			event.preventDefault();
       		event.stopImmediatePropagation();
		});		
		Z._createContextMenu();
	},
	
	_createContextMenu: function() {
		if (!jslet.ui.Menu) {
			return;
		}
		var Z = this;
		var menuCfg = {type: 'Menu', onItemClick: jQuery.proxy(Z._menuItemClick, Z), items: []};
		menuCfg.items.push({id: 'selectAll', name: jsletlocale.DBTable.selectAll});
		menuCfg.items.push({id: 'copy', name: jsletlocale.DBTable.copy});
		menuCfg.items.push({name: '-'});
		if(Z._hasFindDialog) {
			menuCfg.items.push({id: 'find', name: jsletlocale.DBTable.find});
		}
		menuCfg.items.push({id: 'fixed', name: jsletlocale.DBTable.fixed, 
			items: [{id: 'fixedCol', name: jsletlocale.DBTable.fixedCol},
			        {id: 'fixedRow', name: jsletlocale.DBTable.fixedRow},
			]});
		if(Z._enableErrorFinding || Z._editable) {
			menuCfg.items.push({name: '-'});
			menuCfg.items.push({id: 'nextError', name: jsletlocale.DBTable.nextError});
		}
		if (Z._onCreateContextMenu) {
			Z._onCreateContextMenu.call(Z, menuCfg.items);
		}
		if (menuCfg.items.length === 0) {
			return;
		}
		Z.contextMenu = jslet.ui.createControl(menuCfg);
		jQuery(Z.el).on('contextmenu', '.jl-tbl-cell', function(event) {
			Z.contextMenu.showContextMenu(event, Z);
		});
	},
	
	_menuItemClick: function(menuCfg, checked) {
		var menuId = menuCfg.id;
		var Z = this;
		if (menuId == 'selectAll') {
			Z.selectAllCells();
		} else if (menuId == 'find') {
			Z.showFindDialog();
		} else if (menuId == 'fixedRow') {
			Z.fixedRows(Z._dataset.recno() + 1);
			Z.renderAll();
		} else if (menuId == 'fixedCol') {
			Z.fixedCols(Z._currColNum + 1);
			Z.renderAll();
		} else if (menuId == 'copy') {
			jslet.ui.info(jsletlocale.DBTable.copyInfo);
		} else if (menuId == 'nextError') {
			Z.gotoNextError();
		}
	},
	
<span id='jslet-ui-AbstractDBTable-method-showFindDialog'>	/**
</span>	 * Show finding record dialog. Press 'Ctrl + F' to call this method.
	 */
	showFindDialog: function() {
		var Z = this;
		if(Z._filterPanel) {
			Z._filterPanel.hide();
		}
		if(!Z._hasFindDialog) {
			return;
		}
		if(!Z._findDialog) {
			Z._findDialog = new jslet.ui.table.FindDialog(Z);
		}
		if(!Z._findDialog.findingField()) {
			var colCfg = Z.innerColumns[Z._currColNum];
			if(colCfg.field) {
				Z._findDialog.findingField(colCfg.field);
			} else {
				//Get first column with field name.
				for(var i = Z._currColNum + 1, len = Z.innerColumns.length; i &lt; len; i++) {
					colCfg = Z.innerColumns[i];
					if(colCfg.field) {
						Z._findDialog.findingField(colCfg.field);
						break;
					}
				}
			}
		}
		if(Z._findDialog.findingField()) {
			Z._findDialog.show(0, Z.headSectionHt);
		}		
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-method-gotoNextError'>	/**
</span>	 * Goto the next error record if dataset exists errors. Press 'Ctrl + E' to call this method.
	 */
	gotoNextError: function() {
		var Z = this;
		if(!Z._dataset.nextError()) {
			Z._dataset.firstError();
		}
		return this;
	},
	
	copySelection: function(fitExcel) {
		var Z = this, 
			selectedText;
		if(fitExcel) {
			selectedText = Z._dataset.selection.getSelectionText('&quot;', true, '\t');
		} else {
			selectedText = Z._dataset.selection.getSelectionText('', false, '\t');
		}
		if(!selectedText) {
			var fldName = Z.getFieldByColNum(Z._currColNum);
			if(fldName) {
				selectedText = Z._dataset.getFieldText(fldName);
			}
		}
		if(selectedText) {
			jslet.Clipboard.putText(selectedText);
			window.setTimeout(function() {Z.el.focus();}, 5);
		}
		return this;
	},
	
	selectAllCells: function() {
		var Z = this,
			fields = [], colCfg, fldName;
		for(var i = 0, len = Z.innerColumns.length; i &lt; len; i++) {
			colCfg = Z.innerColumns[i];
			fldName = colCfg.field;
			if(fldName) {
				fields.push(fldName);
			}
		}
		Z._dataset.selection.selectAll(fields, true);
		Z._refreshSelection();
		return this;
	},
	
<span id='jslet-ui-AbstractDBTable-method-tabPrior'>	/**
</span>	 * Move cursor to the prior editable field.
	 */
	tabPrior: function() {
		var Z = this,
			fldName = Z.getFieldByColNum(Z._currColNum),
			lastColNum, num = null, 
			focusedFields = Z._dataset.focusedFields(),
			editingFields = Z._getEditingFields();
		if(editingFields &amp;&amp; editingFields.indexOf(fldName) &gt;= 0) {
			var focusMngr = jslet.ui.globalFocusManager,
				onChangingFocusFn = focusMngr.onChangingFocus();
			
			if(focusedFields) {
				focusedFields = focusedFields.slice(0);
				for(var i = focusedFields.length - 1; i &gt;= 0; i--) {
					if(editingFields.indexOf(focusedFields[i]) &lt; 0) {
						focusedFields.splice(i, 1);
					}
				}
			}
			var idx = -1, fields;
			if(focusedFields) {
				fields = focusedFields;
				idx = fields.indexOf(fldName);
			}
			if(idx &lt; 0) {
				fields = editingFields;
				idx = fields.indexOf(fldName);
			}
			if(onChangingFocusFn) {
				var cancelFocus = onChangingFocusFn(document.activeElement || Z.el, true, Z._dataset, 
						focusMngr.activeField(), fields, focusMngr.activeValueIndex());
				if(!cancelFocus) {
					return true;
				}
			}
			if(fields &amp;&amp; idx &gt;= 0) {
				if(idx === 0) {
					if(Z._dataset.recno() &gt; 0) {
						Z._dataset.prior();
						fldName = fields[fields.length - 1];
					} else {
						return false;
					}
				} else {
					fldName = fields[idx - 1];
				}
				num = Z.getColNumByField(fldName);
			}
		}
		if(num === null) {
			lastColNum = Z.innerColumns.length - 1;
			if(Z._currColNum === 0) {
				if(Z._dataset.recno() &gt; 0) {
					Z._dataset.prior();
					num = lastColNum;
				} else {
					return false;
				}
			} else {
				num = Z._currColNum - 1;
			}
		}
		Z.currColNum(num);
		return true;
	},
	
<span id='jslet-ui-AbstractDBTable-method-tabNext'>	/**
</span>	 * Move cursor to the next editable field.
	 */
	tabNext: function() {
		var Z = this,
			fldName = Z.getFieldByColNum(Z._currColNum),
			lastColNum, num = null, 
			focusedFields = Z._dataset.focusedFields(),
			editingFields = Z._getEditingFields();
			
		if(editingFields &amp;&amp; editingFields.indexOf(fldName) &gt;= 0) {
			var focusMngr = jslet.ui.globalFocusManager,
				onChangingFocusFn = focusMngr.onChangingFocus();
			
			if(focusedFields) {
				focusedFields = focusedFields.slice(0);
				for(var i = focusedFields.length - 1; i &gt;= 0; i--) {
					if(editingFields.indexOf(focusedFields[i]) &lt; 0) {
						focusedFields.splice(i, 1);
					}
				}
			}
			var idx = -1, fields;
			if(focusedFields) {
				fields = focusedFields;
				idx = fields.indexOf(fldName);
			}
			if(idx &lt; 0) {
				fields = editingFields;
				idx = fields.indexOf(fldName);
			}
			if(onChangingFocusFn) {
				var cancelFocus = onChangingFocusFn(document.activeElement || Z.el, false, Z._dataset, 
						focusMngr.activeField(), fields, focusMngr.activeValueIndex());
				if(!cancelFocus) {
					return true;
				}
			}
			if(fields &amp;&amp; idx &gt;= 0) {
				if(idx === fields.length - 1) {
					if(Z._dataset.recno() &lt; Z._dataset.recordCount() - 1) {
						Z._dataset.next();
						fldName = fields[0];
					} else {
						return false;
					}
				} else {
					fldName = fields[idx + 1];
				}
				num = Z.getColNumByField(fldName);
			}
		}
		if(num === null) {
			lastColNum = Z.innerColumns.length - 1;
			if(Z._currColNum &lt; lastColNum) {
				num = Z._currColNum + 1;
			} else {
				if(Z._dataset.recno() === Z._dataset.recordCount() - 1) {
					return false;
				}
				Z._dataset.next();
				num = 0;
			}
		}
		Z.currColNum(num);
		return true;
	},
	
	movePriorCell: function(ctrlKey, shiftKey, altKey) {
		var Z = this,
			lastColNum = Z.innerColumns.length - 1,
			num;
		
		if(Z._currColNum === 0) {
			if(Z._dataset.recno() &gt; 0) {
				Z._dataset.prior();
				num = lastColNum;
			} else {
				return;
			}
		} else {
			num = Z._currColNum - 1;
		}
		Z._doBeforeSelect(ctrlKey, shiftKey, altKey);
		Z.currColNum(num);
		Z._processSelection(ctrlKey, shiftKey, altKey);
		return this;
	},
	
	moveNextCell: function(ctrlKey, shiftKey, altKey) {
		var Z = this,
			lastColNum = Z.innerColumns.length - 1,
			num;
		
		if(Z._currColNum &lt; lastColNum) {
			num = Z._currColNum + 1;
		} else {
			if(Z._dataset.recno() === Z._dataset.recordCount() - 1) {
				return;
			}
			Z._dataset.next();
			num = 0;
		}
		Z._doBeforeSelect(ctrlKey, shiftKey, altKey);
		Z.currColNum(num);
		Z._processSelection(ctrlKey, shiftKey, altKey);
		return this;
	},
	
	_getEditingFields: function() {
		var Z = this; 
		if(!Z._editable) {
			return null;
		}
		var fldName, fldObj,
			dsObj = Z._dataset,
			result = null;

		for(var i = 0, len = Z.innerColumns.length; i &lt; len; i++) {
			fldName = Z.innerColumns[i].field;
			if(!fldName) {
				continue;
			}
			fldObj = dsObj.getField(fldName);
			if(!fldObj.disabled() &amp;&amp; !fldObj.readOnly()) {
				if(!result) {
					result = [];
				}
				result.push(fldName);
			}
		}
		return result;
	},
	
	_initializeVm: function() {
		var Z = this;

		Z.listvm = new jslet.ui.ListViewModel(Z._dataset, Z._treeField ? true : false);
		
		Z.listvm.onTopRownoChanged = function(rowno) {
			if (rowno &lt; 0) {
				return;
			}
			Z._fillData();
			
			Z._syncScrollBar(rowno);
			Z._showCurrentRow();
		};
	
		Z.listvm.onVisibleCountChanged = function() {
			Z.renderAll();
		};
		
		Z.listvm.onCurrentRownoChanged = function(preRowno, rowno) {
			Z._hideCurrentRow();
			if (Z._dataset.recordCount() === 0) {
				Z._currRow = null;
				return;
			}
			Z._dataset.recno(Z.listvm.getCurrentRecno());
			var currRow = Z._getTrByRowno(rowno), otr;
			if (!currRow) {
				return;
			}
			otr = currRow.fixed;
			if (otr) {
				jQuery(otr).addClass(jslet.ui.htmlclass.TABLECLASS.currentrow);
			}
			
			otr = currRow.content;
			jQuery(otr).addClass(jslet.ui.htmlclass.TABLECLASS.currentrow);
			Z._currRow = currRow;
			if(Z._editable) {
				var fldName = Z._editingField;
				if(fldName) {
					var fldObj = Z._dataset.getField(fldName);
					if(fldObj &amp;&amp; !fldObj.disabled() &amp;&amp; !fldObj.readOnly()) {
						Z._dataset.focusEditControl(fldName);
					}
				}
			}
		};
	},
	
<span id='jslet-ui-AbstractDBTable-method-renderAll'>	/**
</span>	 * @override
	 */
	renderAll: function() {
		var Z = this;
		Z._innerDestroy();
		Z.el.innerHTML = '';
		Z.listvm.fixedRows = Z._fixedRows;
		Z._calcParams();
		Z.listvm.refreshModel();
		Z._createFrame();
		Z._fillData();
		Z._showCurrentRow(true);
		Z._oldHeight = jQuery(Z.el).height();
		Z._updateSortFlag(true);
		return this;
	}, // end renderAll

	_doBeforeSelect: function(hasCtrlKey, hasShiftKey, hasAltKey) {
	},
	
	_getSelectionFields: function(startColNum, endColNum) {
		if(startColNum &gt; endColNum) {
			var tmp = startColNum;
			startColNum = endColNum;
			endColNum = tmp;
		}
		var fields = [], fldName, colCfg, colNum;
		for(var i = 0, len = this.innerColumns.length; i &lt; len; i++) {
			colCfg = this.innerColumns[i];
			colNum = colCfg.colNum;
			if(colNum &gt;= startColNum &amp;&amp; colNum &lt;= endColNum) {
				fldName = colCfg.field;
				if(fldName) {
					fields.push(fldName);
				}
			}
		}
		return fields;
	},
	
	_processSelection: function(hasCtrlKey, hasShiftKey, hasAltKey) {
		var Z = this,
			currRecno = Z._dataset.recno(),
			fldName;
		if(hasCtrlKey || !Z._autoClearSelection) { //If autoClearSelection = false, click a cell will select it.
			fldName = Z.innerColumns[Z._currColNum].field;
			if(fldName) {
				if(Z._dataset.selection.isSelected(currRecno, fldName)) {
					Z._dataset.selection.remove(currRecno, currRecno, [fldName], true);
				} else {
					Z._dataset.selection.add(currRecno, currRecno, [fldName], true);
				}
				Z._refreshSelection();
			}
			Z._preRecno = currRecno;
			Z._preColNum = Z._currColNum;
			return;
		}
		if(hasShiftKey) {
			var fields;
			if(Z._preTmpRecno &gt;= 0 &amp;&amp; Z._preTmpColNum &gt;= 0) {
				fields = Z._getSelectionFields(Z._preColNum || 0, Z._preTmpColNum);
				Z._dataset.selection.remove(Z._preRecno || 0, Z._preTmpRecno, fields, false);
			}
			fields = Z._getSelectionFields(Z._preColNum || 0, Z._currColNum);
			Z._dataset.selection.add(Z._preRecno || 0, currRecno, fields, true);
			Z._refreshSelection();
			Z._preTmpRecno = currRecno;
			Z._preTmpColNum = Z._currColNum;
		} else {
			Z._preRecno = currRecno;
			Z._preColNum = Z._currColNum;
			Z._preTmpRecno = currRecno;
			Z._preTmpColNum = Z._currColNum;
			if(Z._autoClearSelection) {
				Z._dataset.selection.removeAll();
				Z._refreshSelection();
			}
		}
	},
	
	_processColumnSelection: function(colCfg, hasCtrlKey, hasShiftKey, hasAltKey) {
		if(!hasCtrlKey &amp;&amp; !hasShiftKey) {
			return;
		}
		var Z = this,
			recCnt = Z._dataset.recordCount();
		if(recCnt === 0) {
			return;
		}
		var fields, colNum;
		if(hasShiftKey) {
			if(Z._preTmpColNum &gt;= 0) {
				fields = Z._getSelectionFields(Z._preColNum || 0, Z._preTmpColNum);
				Z._dataset.selection.remove(0, recCnt, fields, true);
			}
			colNum = colCfg.colNum + colCfg.colSpan - 1;
			fields = Z._getSelectionFields(Z._preColNum || 0, colNum);
			Z._dataset.selection.add(0, recCnt, fields, true);
			Z._preTmpColNum = colNum;
		} else {
			if(!hasCtrlKey &amp;&amp; Z._autoClearSelection) {
				Z._dataset.selection.removeAll();
			}
			if(colCfg.colSpan &gt; 1) {
				fields = [];
				var startColNum = colCfg.colNum,
					endColNum = colCfg.colNum + colCfg.colSpan, fldName;
				
				for(colNum = startColNum; colNum &lt; endColNum; colNum++) {
					fldName = Z.innerColumns[colNum].field;
					fields.push(fldName);
				}
			} else {
				fields = [colCfg.field];
			}
			Z._dataset.selection.add(0, recCnt, fields, true);
			Z._preColNum = colCfg.colNum;
		}
		Z._refreshSelection();
	},
	
	_processRowSelection: function(hasCtrlKey, hasShiftKey, hasAltKey) {
		if(!hasCtrlKey &amp;&amp; !hasShiftKey) {
			return;
		}
		var Z = this,
			fields = Z._getSelectionFields(0, Z.innerColumns.length - 1);
		var currRecno = Z._dataset.recno();
		if(hasShiftKey) {
			if(Z._preTmpRecno &gt;= 0) {
				Z._dataset.selection.remove(Z._preRecno || 0, Z._preTmpRecno, fields, true);
			}
			Z._dataset.selection.add(Z._preRecno || 0, currRecno, fields, true);
			Z._preTmpColNum = currRecno;
		} else {
			if(!hasCtrlKey &amp;&amp; Z._autoClearSelection) {
				Z._dataset.selection.removeAll();
			}
			Z._dataset.selection.add(currRecno, currRecno, fields, true);
			Z._preRecno = currRecno;
		}
		Z._refreshSelection();
	},
	
	_prepareColumn: function() {
		var Z = this, cobj;
		Z._sysColumns = [];
		//prepare system columns
		if (Z._hasSeqCol) {
			cobj = {label:'&amp;nbsp;',width: Z.seqColWidth, disableHeadSort:true,isSeqCol:true, 
					cellRender:jslet.ui.table.cellRenders.sequenceCellRender, widthCssName: Z._widthStyleId + '-s0'};
			Z._sysColumns.push(cobj);
		}
		if (Z._hasSelectCol) {
			cobj = {label:'&lt;input type=&quot;checkbox&quot; /&gt;', width: Z.selectColWidth, disableHeadSort:true,isSelectCol:true, 
					cellRender:jslet.ui.table.cellRenders.selectCellRender, widthCssName: Z._widthStyleId + '-s1'};
			Z._sysColumns.push(cobj);
		}
		
		if (Z.subgroup &amp;&amp; Z.subgroup.hasExpander) {
			cobj = {label:'&amp;nbsp;', width: Z.selectColWidth, disableHeadSort:true, isSubgroup: true, 
					cellRender:jslet.ui.table.cellRenders.subgroupCellRender, widthCssName: Z._widthStyleId + '-s2'};
			Z._sysColumns.push(cobj);
		}
		//prepare data columns
		var tmpColumns = [], fldObj;
		if (Z._columns) {
			for(var k = 0, colCnt2 = Z._columns.length; k &lt; colCnt2; k++) {
				cobj = Z._columns[k];
				if (!cobj.field) {
					cobj.disableHeadSort = true;
				} else {
					fldObj = Z._dataset.getField(cobj.field);
					if(!fldObj) {
						throw new Error('Not found Field: ' + cobj.field);
					}
					cobj.displayOrder = fldObj.displayOrder();
				}
				tmpColumns.push(cobj);
			}
		}
		function getColumnObj(fldName) {
			if (Z._columns) {
				for(var m = 0, colCnt1 = Z._columns.length; m &lt; colCnt1; m++) {
					cobj = Z._columns[m];
					if (cobj.field &amp;&amp; cobj.field == fldName) {
						return cobj;
					}
				}
			}
			return null;
		}
		var i, fldcnt, colCnt, fldName;
		if (!Z._onlySpecifiedCol) {
			var fields = Z._dataset.getFields();
			for (i = 0, fldcnt = fields.length; i &lt; fldcnt; i++) {
				fldObj = fields[i];
				fldName = fldObj.name();
				if (fldObj.visible()) {
					cobj = getColumnObj(fldName);
					if(!cobj) {
						cobj = new jslet.ui.table.TableColumn();
						cobj.field = fldObj.name();
						cobj.displayOrder = fldObj.displayOrder();
						tmpColumns.push(cobj);
					}
				} // end if visible
			} // end for
			if (Z._columns) {
				for(i = 0, colCnt = Z._columns.length; i &lt; colCnt; i++) {
					cobj = Z._columns[i];
					if (!cobj.field) {
						continue;
					}
					fldObj = Z._dataset.getTopField(cobj.field);
					if (!fldObj) {
						throw new Error(&quot;Field: &quot; + cobj.field + &quot; doesn't exist!&quot;);
					}
					var children = fldObj.children();
					if (children &amp;&amp; children.length &gt; 0) {
						fldName = fldObj.name();
						var isUnique = true;
						// cobj.field is not a child of a groupfield, we need check if the topmost parent field is duplicate or not 
						if (cobj.field != fldName) {
							for(var n = 0; n &lt; tmpColumns.length; n++) {
								if (tmpColumns[n].field == fldName) {
									isUnique = false;
									break;
								}
							} // end for k
						}
						if (isUnique) {
							cobj = new jslet.ui.table.TableColumn();
							cobj.field = fldName;
							cobj.displayOrder = fldObj.displayOrder();
							tmpColumns.push(cobj);
						}
					}
				} //end for i
			} //end if Z.columns
		}
		
		tmpColumns.sort(function(cobj1, cobj2) {
			var ord1 = cobj1.displayOrder || 0;
			var ord2 = cobj2.displayOrder || 0;
			return ord1 === ord2? 0: (ord1 &lt; ord2? -1: 1);
		});
		
		Z.innerHeads = [];
		Z.innerColumns = [];
		var ohead, label, 
			context = {lastColNum: 0, depth: 0};
		
		for(i = 0, colCnt = tmpColumns.length; i &lt; colCnt; i++) {
			cobj = tmpColumns[i];
			fldName = cobj.field;
			if (!fldName) {
				ohead = new jslet.ui.table.TableHead();
				label = cobj.label;
				ohead.label = label? label: &quot;&quot;;
				ohead.level = 0;
				ohead.colNum = context.lastColNum++;
				cobj.colNum = ohead.colNum;
				ohead.id = jslet.nextId();
				ohead.widthCssName = Z._widthStyleId + '-' + ohead.colNum;
				cobj.widthCssName = ohead.widthCssName;
				ohead.disableHeadSort = cobj.disableHeadSort;

				Z.innerHeads.push(ohead);
				Z.innerColumns.push(cobj);
				
				continue;
			}
			fldObj = Z._dataset.getField(fldName);
			Z._convertField2Head(context, fldObj);
		}

		Z.maxHeadRows = context.depth + 1;
		Z._calcHeadSpan();
	
		//check fixedCols property
		var preColCnt = 0, len,
			fixedColNum = Z._fixedCols - Z._sysColumns.length;
		colCnt = 0;
		for(i = 1, len = Z.innerHeads.length; i &lt; len; i++) {
			ohead = Z.innerHeads[i];
			colCnt += ohead.colSpan;
			if (fixedColNum &lt;= preColCnt || fixedColNum == colCnt) {
				break;
			}
			if (fixedColNum &lt; colCnt &amp;&amp; fixedColNum &gt; preColCnt) {
				Z._fixedCols = preColCnt + Z._sysColumns.length;
			}
			
			preColCnt = colCnt;
		}
	},
	
	_calcHeadSpan: function(heads) {
		var Z = this;
		if (!heads) {
			heads = Z.innerHeads;
		}
		var ohead, childCnt = 0;
		for(var i = 0, len = heads.length; i &lt; len; i++ ) {
			ohead = heads[i];
			ohead.rowSpan = Z.maxHeadRows - ohead.level;
			if (ohead.subHeads) {
				ohead.colSpan = Z._calcHeadSpan(ohead.subHeads);
				childCnt += ohead.colSpan;
			} else {
				ohead.colSpan = 1;
				childCnt++;
			}
		}
		return childCnt;
	},
	
	_convertField2Head: function(context, fldObj, parentHeadObj) {
		function checkHtml(htmlStr) {
		    var  reg = /&lt;[^&gt;]+&gt;/g;
		    return reg.test(htmlStr);
		}
		
		var Z = this;
		if (!fldObj.visible()) {
			return false;
		}
		var level = 0, heads;
		if (!parentHeadObj) {
			heads = Z.innerHeads;
		} else {
			level = parentHeadObj.level + 1;
			heads = parentHeadObj.subHeads;
		}
		var ohead, fldName = fldObj.name();
		ohead = new jslet.ui.table.TableHead();
		ohead.label = fldObj.displayLabel();
		ohead.field = fldName;
		ohead.level = level;
		ohead.colNum = context.lastColNum;
		ohead.id = jslet.nextId();
		heads.push(ohead);
		context.depth = Math.max(level, context.depth);
		var fldChildren = fldObj.children();
		if (fldChildren &amp;&amp; fldChildren.length &gt; 0) {
			ohead.subHeads = [];
			var added = false;
			for(var i = 0, cnt = fldChildren.length; i&lt; cnt; i++) {
				Z._convertField2Head(context, fldChildren[i], ohead);
			}
		} else {
			context.lastColNum ++;
			var cobj, found = false;
			var len = Z._columns ? Z._columns.length: 0;
			for(var k = 0; k &lt; len; k++) {
				cobj = Z._columns[k];
				if (cobj.field == fldName) {
					found = true;
					break;
				}
			}
			if (!found) {
				cobj = new jslet.ui.table.TableColumn();
				cobj.field = fldName;
			}
			if (!cobj.label) {
				cobj.label = fldObj.displayLabel();
			}
			var dataType = fldObj.getType();
			if(dataType === jslet.data.DataType.BOOLEAN) {
				cobj.isBoolColumn = true;
			}
			cobj.mergeSame = fldObj.mergeSame();
			cobj.colNum = ohead.colNum;
			if (!cobj.width) {
				var maxWidth = fldObj ? fldObj.displayWidth() : 0;
				if (!Z._hideHead &amp;&amp; cobj.label) {
					if(checkHtml(cobj.label)) {
						maxWidth = Math.max(maxWidth, jQuery(cobj.label).text().length);
					} else {
						maxWidth = Math.max(maxWidth, cobj.label.length);
					}
				}
				cobj.width = maxWidth ? (maxWidth * Z.charWidth) : 10;
			}
			//check and set cell render 
			if (!cobj.cellRender) {
				if (dataType === jslet.data.DataType.BOOLEAN) {//data type is boolean
					cobj.cellRender = jslet.ui.table.cellRenders.boolCellRender;
				} else if (dataType === jslet.data.DataType.EDITACTION) {//data type is edit
					ohead.cellRender = cobj.cellRender = jslet.ui.table.cellRenders.editActionCellRender;
				} else if (cobj.field == Z._treeField) {
					cobj.cellRender = jslet.ui.table.cellRenders.treeCellRender;
				}
			}
			if(dataType === jslet.data.DataType.ACTION || dataType === jslet.data.DataType.EDITACTION) {
				ohead.disableHeadSort = true;
				ohead.disableFilter = true;
			}
			ohead.widthCssName = Z._widthStyleId + '-' + ohead.colNum;
			cobj.widthCssName = ohead.widthCssName;
			
			Z.innerColumns.push(cobj);
		}
		return true;
	},
	
	_calcParams: function() {
		var Z = this;
		Z._currColNum = 0;
		Z._preTmpColNum = -1;
		Z._preTmpRecno = -1;
		Z._preRecno = -1;
		Z._preColNum = -1;

		if (Z._treeField) {//if tree style table, it can't be sorted by clicking column header
			Z._disableHeadSort = true;
		}
		// calculate Sequence column width
		if (Z._hasSeqCol) {
			Z.seqColWidth = ('' + Z._dataset.recordCount()).length * Z.charWidth + 5;
			var sWidth = jslet.ui.htmlclass.TABLECLASS.selectColWidth;
			Z.seqColWidth = Z.seqColWidth &gt; sWidth ? Z.seqColWidth: sWidth;
		} else {
			Z.seqColWidth = 0;
		}
		// calculate Select column width
		if (Z._hasSelectCol) {
			Z.selectColWidth = jslet.ui.htmlclass.TABLECLASS.selectColWidth;
		} else {
			Z.selectColWidth = 0;
		}
		//calculate Fixed row section's height
		if (Z._fixedRows &gt; 0) {
			Z.fixedSectionHt = Z._fixedRows * Z.rowHeight();
		} else {
			Z.fixedSectionHt = 0;
		}
		//Calculate Foot section's height
		if (Z.aggregated() &amp;&amp; Z.dataset().checkAggregated()) {
			Z.footSectionHt = Z.footerRowHeight();
		} else {
			Z.footSectionHt = 0;
		}
		Z._prepareColumn();

		// fixed Column count must be less than total columns
		if (Z._fixedCols) {
			if (Z._fixedCols &gt; Z.innerColumns.length) {
				Z._fixedCols = Z.innerColumns.length;
			}
		}
		Z.hasFixedCol = Z._sysColumns.length &gt; 0 || Z._fixedCols &gt; 0;
		if (Z.hasFixedCol) {
			var w = 0, i, cnt;
			for(i = 0, cnt = Z._sysColumns.length; i &lt; cnt; i++) {
				w += Z._sysColumns[i].width + 1;
			}
			for(i = 0, cnt = Z._fixedCols; i &lt; cnt; i++) {
				w += Z.innerColumns[i].width + 1;
			}
			Z.fixedColWidth = w + 1;
		} else {
			Z.fixedColWidth = 0;
		}
	}, // end _calcParams

	_setScrollBarMaxValue: function(maxValue) {
		var Z = this,
			v = maxValue + Z._repairHeight + (Z._autoStretch? Z.footSectionHt: 0);
		Z.jqVScrollBar.find('div').height(v);
		if(Z.contentSectionHt &gt;= v) {
			Z.jqVScrollBar.parent().addClass('jl-scrollbar-hidden');	
		} else {
			Z.jqVScrollBar.parent().removeClass('jl-scrollbar-hidden');	
		}
	},

	_changeColWidthCssRule: function(cssName, width) {
		var Z = this,
			styleEle = document.getElementById(Z._widthStyleId),
			styleObj = styleEle.styleSheet || styleEle.sheet,
			cssRules = styleObj.cssRules || styleObj.rules,
			cssRule = null, found = false;
			cssName = '.' + cssName;
		for(var i = 0, len = cssRules.length; i &lt; len; i++) {
			cssRule = cssRules[i];
			if(cssRule.selectorText == cssName) {
				found = true;
				break;
			}
		}
		if(found) {
			cssRule.style.width = width + 'px';
		}
		return found;
	},

	_changeColWidth: function(index, deltaW) {
		var Z = this,
			colObj = Z.innerColumns[index];
		if (colObj.width + deltaW &lt;= 0) {
			return;
		}
		colObj.width += deltaW;
		if(colObj.field) {
			Z._dataset.designMode(true);
			try {
				Z._dataset.getField(colObj.field).displayWidth(Math.round(colObj.width/Z.charWidth));
			} finally {
				Z._dataset.designMode(false);
			}
		}
		if(Z._changeColWidthCssRule(colObj.widthCssName, colObj.width)) {
			Z._changeContentWidth(deltaW);
		}
	},

	_refreshSeqColWidth: function() {
		var Z = this;
		if (!Z._hasSeqCol) {
			return;
		}
		var oldSeqColWidth = Z.seqColWidth;
		Z.seqColWidth = ('' + Z._dataset.recordCount()).length * Z.charWidth;
		var sWidth = jslet.ui.htmlclass.TABLECLASS.selectColWidth;
		Z.seqColWidth = Z.seqColWidth &gt; sWidth ? Z.seqColWidth: sWidth;
		if(Z.seqColWidth == oldSeqColWidth) {
			return;
		}
		var colObj;
		for(var i = 0, len = Z._sysColumns.length; i &lt; len; i++) {
			colObj = Z._sysColumns[i];
			if(colObj.isSeqCol) {
				break;
			}
		}
		colObj.width = Z.seqColWidth;
		Z._changeColWidthCssRule(colObj.widthCssName, Z.seqColWidth);
		var deltaW = Z.seqColWidth - oldSeqColWidth;
		Z._changeContentWidth(deltaW, true);
	},

	_changeContentWidth: function(deltaW, isLeft) {
		var Z = this,
			totalWidth = Z.getTotalWidth(isLeft),
			totalWidthStr = totalWidth + 'px';
		if(!isLeft) {
			Z.rightHeadTbl.parentNode.style.width = totalWidthStr;
			Z.rightFixedTbl.parentNode.style.width = totalWidthStr;
			Z.rightContentTbl.parentNode.style.width = totalWidthStr;
			if (Z.footSectionHt) {
				Z.rightFootTbl.style.width = totalWidthStr;
			}
		} else {
			Z.fixedColWidth = totalWidth;
			Z.leftHeadTbl.parentNode.parentNode.style.width = Z.fixedColWidth + 1 + 'px';
			Z.leftHeadTbl.parentNode.style.width = totalWidthStr;
			Z.leftFixedTbl.parentNode.style.width = totalWidthStr;
			Z.leftContentTbl.parentNode.style.width = totalWidthStr;
		}
		Z._checkHoriOverflow();
	},

	_createFrame: function() {
		var Z = this;
		Z.el.style.position = 'relative';
		var jqEl = jQuery(Z.el);
		if (!jqEl.hasClass('jl-table')) {
			jqEl.addClass('jl-table jl-border-box jl-round5');
		}
		if(Z._noborder) {
			jqEl.addClass('jl-tbl-noborder');
		}
		
		function generateWidthStyle() {
			var colObj, cssName, i, len,
				styleHtml = ['&lt;style type=&quot;text/css&quot; id=&quot;' + Z._widthStyleId + '&quot;&gt;\n'];
			for(i = 0, len = Z._sysColumns.length; i &lt; len; i++) {
				colObj = Z._sysColumns[i];
				styleHtml.push('.' + colObj.widthCssName +'{width:' + colObj.width + 'px}\n');
			}
			for(i = 0, len = Z.innerColumns.length; i&lt; len; i++) {
				colObj = Z.innerColumns[i];
				styleHtml.push('.' + colObj.widthCssName +'{width:' + colObj.width + 'px}\n');
			}
			styleHtml.push('&lt;/style&gt;');
			return styleHtml.join('');
		}
		
		var dbtableframe = [
			'&lt;div class=&quot;jl-tbl-splitter&quot; style=&quot;display: none&quot;&gt;&lt;/div&gt;',
			generateWidthStyle(),
			'&lt;div class=&quot;jl-tbl-norecord&quot;&gt;',
			jsletlocale.DBTable.norecord,
			'&lt;/div&gt;',
			'&lt;table class=&quot;jl-tbl-container&quot;&gt;&lt;tr&gt;',
			'&lt;td&gt;&lt;div class=&quot;jl-tbl-fixedcol&quot;&gt;&lt;table class=&quot;jl-tbl-data&quot;&gt;&lt;tbody /&gt;&lt;/table&gt;&lt;table class=&quot;jl-tbl-data&quot;&gt;&lt;tbody /&gt;&lt;/table&gt;&lt;div class=&quot;jl-tbl-content-div&quot;&gt;&lt;table class=&quot;jl-tbl-data&quot;&gt;&lt;tbody /&gt;&lt;/table&gt;&lt;table&gt;&lt;tbody /&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;',
			'&lt;td&gt;&lt;div class=&quot;jl-tbl-contentcol&quot;&gt;&lt;div&gt;&lt;table class=&quot;jl-tbl-data jl-tbl-content-table&quot; border=&quot;0&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot;&gt;&lt;tbody /&gt;&lt;/table&gt;&lt;/div&gt;&lt;div&gt;&lt;table class=&quot;jl-tbl-data jl-tbl-content-table&quot;&gt;&lt;tbody /&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class=&quot;jl-tbl-content-div&quot;&gt;&lt;table class=&quot;jl-tbl-data jl-tbl-content-table&quot;&gt;&lt;tbody /&gt;&lt;/table&gt;&lt;table class=&quot;jl-tbl-content-table jl-tbl-footer&quot;&gt;&lt;tbody /&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;',
			'&lt;td class=&quot;jl-scrollbar-col&quot;&gt;&lt;div class=&quot;jl-tbl-vscroll-head&quot;&gt;&lt;/div&gt;&lt;div class=&quot;jl-tbl-vscroll&quot;&gt;&lt;div /&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;'];
		
		jqEl.html(dbtableframe.join(''));

		var children = jqEl.find('.jl-tbl-fixedcol')[0].childNodes;
		Z.leftHeadTbl = children[0];
		Z.leftFixedTbl = children[1];
		Z.leftContentTbl = children[2].firstChild;
		Z.leftFootTbl = children[2].children[1];
		
		children = jqEl.find('.jl-tbl-contentcol')[0].childNodes;
		Z.rightHeadTbl = children[0].firstChild;
		Z.rightFixedTbl = children[1].firstChild;
		Z.rightContentTbl = children[2].firstChild;
		Z.rightFootTbl = children[2].children[1];

		Z.height = jqEl.height();
		if(Z.height &lt;= 1 &amp;&amp; !Z._autoStretch) {
			Z.height = 200;
			jqEl.height(200);
		}
		if (Z._hideHead) {
			Z.leftHeadTbl.style.display = 'none';
			Z.rightHeadTbl.style.display = 'none';
			jqEl.find('.jl-tbl-vscroll-head').css('display', 'none');
		}
		if (Z._fixedRows &lt;= 0) {
			Z.leftFixedTbl.style.display = 'none';
			Z.rightFixedTbl.style.display = 'none';
		}
		if (!Z.footSectionHt) {
			Z.leftFootTbl.style.display = 'none';
			Z.rightFootTbl.style.display = 'none';
		}
		Z.leftHeadTbl.parentNode.parentNode.style.width = Z.fixedColWidth + 'px';
		
		var jqRightHead = jQuery(Z.rightHeadTbl);
		jqRightHead.off();
		var x = jqRightHead.on('mousedown', Z._doSplitHookDown);
		var y = jqRightHead.on('mouseup', Z._doSplitHookUp);
		
		jQuery(Z.leftHeadTbl).on('mousedown', '.jl-tbl-header-cell', function(event) {
			event = jQuery.event.fix(event || window.event);
			if(event.button) {
				return;
			}
			var el = event.target;
			if (el.className == 'jl-tbl-splitter-hook') {
				return;
			}
			var colCfg = this.jsletColCfg;
			if(colCfg.field) {
				Z._processColumnSelection(colCfg, event.ctrlKey, event.shiftKey, event.altKey);
			}
		});
		
		jqRightHead.on('mousedown', '.jl-tbl-header-cell', function(event) {
			if(event.button) {
				return;
			}
			event = jQuery.event.fix(event || window.event);
			var el = event.target;
			if (el.className == 'jl-tbl-splitter-hook') {
				return;
			}
			var colCfg = this.jsletColCfg;
			if(colCfg.field) {
				Z._processColumnSelection(colCfg, event.ctrlKey, event.shiftKey, event.altKey);
			}
		});

		jQuery(Z.leftHeadTbl).on('mouseup', '.jl-focusable-item', function(event) {
			if(event.button) {
				return;
			}
			event = jQuery.event.fix(event || window.event);
			var el = event.target;
			if (Z.isDraggingColumn) {
				return;
			}
			Z._doHeadClick(this.parentNode.parentNode.parentNode.jsletColCfg, event.ctrlKey);
			Z._head_label_cliecked = true;
			event.stopImmediatePropagation();
			event.preventDefault();
			return false;
		});
		
		jqRightHead.on('mouseup', '.jl-focusable-item', function(event) {
			if(event.button) {
				return;
			}
			event = jQuery.event.fix(event || window.event);
			var el = event.target;
			if (Z.isDraggingColumn) {
				return;
			}
			Z._doHeadClick(this.parentNode.parentNode.parentNode.jsletColCfg, event.ctrlKey);
			Z._head_label_cliecked = true;
			event.stopImmediatePropagation();
			event.preventDefault();
			return false;
		});
		
		var dragTransfer = null;
		jqRightHead.on('dragstart', '.jl-focusable-item', function(event) {
			if(Z._filterPanel) {
				Z._filterPanel.hide();
			}
			var otd = this.parentNode.parentNode.parentNode,
				colCfg = otd.jsletColCfg,
				e = event.originalEvent,
				transfer = e.dataTransfer;
			transfer.dropEffect = 'link';
			transfer.effectAllowed = 'link';
			dragTransfer = {fieldName: colCfg.field, rowIndex: otd.parentNode.rowIndex, cellIndex: otd.cellIndex};
			transfer.setData('fieldName', colCfg.field); //must has this row otherwise FF does not work.
			return true;
		});

		function checkDropable(currCell) {
			var colCfg = currCell.jsletColCfg,
				srcRowIndex = dragTransfer.rowIndex,
				srcCellIndex = dragTransfer.cellIndex,
				currRowIndex = currCell.parentNode.rowIndex,
				currCellIndex = currCell.cellIndex;
			var result = (srcRowIndex == currRowIndex &amp;&amp; currCellIndex != srcCellIndex);
			if(!result) {
				return result;
			}
			var	srcFldName = dragTransfer.fieldName,
				currFldName = colCfg.field,
				srcPFldObj = Z._dataset.getField(srcFldName).parent(),
				currPFldObj = currFldName &amp;&amp; Z._dataset.getField(currFldName).parent();
			result = (srcPFldObj === currPFldObj || (currPFldObj &amp;&amp; srcPFldObj.name() == currPFldObj.name()));
			return result;
		}
		
		jqRightHead.on('dragover', '.jl-tbl-header-cell .jl-tbl-header-div', function(event) {
			if(!dragTransfer) {
				return false;
			}
			var otd = this.parentNode,
				e = event.originalEvent,
				transfer = e.dataTransfer;
			if(checkDropable(otd)) { 
				jQuery(event.currentTarget).addClass('jl-tbl-col-over');
				transfer.dropEffect = 'link';
			} else {
				transfer.dropEffect = 'move';
			}
		    return false;
		});

		jqRightHead.on('dragenter', '.jl-tbl-header-cell .jl-tbl-header-div', function(event) {
			if(!dragTransfer) {
				return false;
			}
			var otd = this.parentNode,
				e = event.originalEvent,
				transfer = e.dataTransfer;
			if(checkDropable(otd)) { 
				jQuery(event.currentTarget).addClass('jl-tbl-col-over');
				transfer.dropEffect = 'link';
			} else {
				transfer.dropEffect = 'move';
			}
		    return false;
		});
		
		jqRightHead.on('dragleave', '.jl-tbl-header-cell .jl-tbl-header-div', function(event) {
			if(!dragTransfer) {
				return false;
			}
			jQuery(event.currentTarget).removeClass('jl-tbl-col-over');
			return  false;
		});
		
		jqRightHead.on('drop', '.jl-tbl-header-cell .jl-tbl-header-div', function(event) {
			if(!dragTransfer) {
				return false;
			}
			jQuery(event.currentTarget).removeClass('jl-tbl-col-over');
			var currCell = this.parentNode,
				e = event.originalEvent,
				transfer = e.dataTransfer,
				colCfg = currCell.jsletColCfg,
				srcRowIndex = dragTransfer.rowIndex,
				srcCellIndex = dragTransfer.cellIndex,
				currRowIndex = currCell.parentNode.rowIndex,
				currCellIndex = currCell.cellIndex;
			
			if(!checkDropable(currCell)) { 
				return;
			}
			var destField = this.parentNode.jsletColCfg.field;
			if(!destField) {
				return;
			}
			var	srcField = dragTransfer.fieldName;
			Z._moveColumn(srcRowIndex, srcCellIndex, currCellIndex);
	    	return false;
		});
		
		var jqLeftFixedTbl = jQuery(Z.leftFixedTbl),
			jqRightFixedTbl = jQuery(Z.rightFixedTbl),
			jqLeftContentTbl = jQuery(Z.leftContentTbl),
			jqRightContentTbl = jQuery(Z.rightContentTbl);
		
		jqLeftFixedTbl.off();
		jqLeftFixedTbl.on('mouseenter', 'tr', function() {
			jQuery(this).addClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			jQuery(jqRightFixedTbl[0].rows[this.rowIndex]).addClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
		});
		jqLeftFixedTbl.on('mouseleave', 'tr', function() {
			jQuery(this).removeClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			jQuery(jqRightFixedTbl[0].rows[this.rowIndex]).removeClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
		});

		jqRightFixedTbl.off();
		jqRightFixedTbl.on('mouseenter', 'tr', function() {
			jQuery(this).addClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			jQuery(jqLeftFixedTbl[0].rows[this.rowIndex]).addClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
		});
		jqRightFixedTbl.on('mouseleave', 'tr', function() {
			jQuery(this).removeClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			jQuery(jqLeftFixedTbl[0].rows[this.rowIndex]).removeClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
		});
		
		jqLeftContentTbl.off();
		jqLeftContentTbl.on('mouseenter', 'tr', function() {
			jQuery(this).addClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			jQuery(jqRightContentTbl[0].rows[this.rowIndex]).addClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
		});
		jqLeftContentTbl.on('mouseleave', 'tr', function() {
			jQuery(this).removeClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			jQuery(jqRightContentTbl[0].rows[this.rowIndex]).removeClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
		});
		
		jqRightContentTbl.off();
		jqRightContentTbl.on('mouseenter', 'tr', function() {
			jQuery(this).addClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			var hasLeft = (Z._fixedRows &gt; 0 || Z._sysColumns.length &gt; 0);
			if(hasLeft) {
				jQuery(jqLeftContentTbl[0].rows[this.rowIndex]).addClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			}
		});
		jqRightContentTbl.on('mouseleave', 'tr', function() {
			jQuery(this).removeClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			var hasLeft = (Z._fixedRows &gt; 0 || Z._sysColumns.length &gt; 0);
			if(hasLeft) {
				jQuery(jqLeftContentTbl[0].rows[this.rowIndex]).removeClass(jslet.ui.htmlclass.TABLECLASS.hoverrow);
			}
		});
		
		Z.jqVScrollBar = jqEl.find('.jl-tbl-vscroll');
		//The scrollbar width must be set explicitly, otherwise it doesn't work in IE. 
		Z.jqVScrollBar.width(jslet.ui.scrollbarSize() + 1);
		
		Z.jqVScrollBar.off().on('scroll', function() {
			if (Z._keep_silence_) {
				return;
			}
			if(Z._editable &amp;&amp; Z._dataset.status() != jslet.data.DataSetStatus.BROWSE) {
				Z._dataset.confirm();
			}
			var num = Math.round(this.scrollTop / Z.rowHeight());// + Z._fixedRows;
			if (num != Z.listvm.getVisibleStartRow()) {
				Z._keep_silence_ = true;
				try {
					Z.listvm.setVisibleStartRow(num);
					Z._showCurrentRow();
					var cellEditor = Z.cellEditor();
					if(cellEditor) {
						cellEditor.hideEditor();
					}
				} finally {
					Z._keep_silence_ = false;
				}
			}

		});

		jqEl.find('.jl-tbl-contentcol').off().on('scroll', function() {
			if(Z._isCurrCellInView()) {
				//Avoid focusing the current control
				jslet.temp.focusing = true;
				try {
					Z._showCurrentCell();
					
				} finally {
					jslet.temp.focusing = false;
				}
			} else {
	        	var cellEditor = Z.cellEditor();
	        	if(cellEditor) {
	       			cellEditor.hideEditor();
	        	}
			}
			if(Z._filterPanel) {
				Z._filterPanel.hide();
			}
		});
		
		var splitter = jqEl.find('.jl-tbl-splitter')[0];
		splitter._doDragStart = function() {
			this.style.display = 'block';
			if(Z._filterPanel) {
				Z._filterPanel.hide();
			}
		};
		
		splitter._doDragging = function(oldX, oldY, x, y, deltaX, deltaY) {
			var bodyMleft = parseInt(jQuery(document.body).css('margin-left'));

			var ojslet = splitter.parentNode.jslet;
			var colObj = ojslet.innerColumns[ojslet.currColId];
			if (colObj.width + deltaX &lt;= 40) {
				return;
			}
			splitter.style.left = x - jQuery(splitter.parentNode).offset().left - bodyMleft + 'px';
		};

		splitter._doDragEnd = function(oldX, oldY, x, y, deltaX,
			deltaY) {
			var Z = splitter.parentNode.jslet;
			var cellEditor = Z.cellEditor();
			if(cellEditor) {
				cellEditor.hideEditor();
			}
			Z._changeColWidth(Z.currColId, deltaX);
			splitter.style.display = 'none';
			splitter.parentNode.jslet.isDraggingColumn = false;
//			if(cellEditor) {
//				cellEditor.showEditor();
//			}

		};

		splitter._doDragCancel = function() {
			splitter.style.display = 'none';
			splitter.parentNode.jslet.isDraggingColumn = false;
		};

		if (Z.footSectionHt) {
			Z.leftFootTbl.style.display = '';
			Z.rightFootTbl.style.display = '';
		}
		Z._renderHeader();
		if (Z._hideHead) {
			Z.headSectionHt = 0;
		} else {
			Z.headSectionHt = Z.maxHeadRows * Z.headRowHeight();
		}
		Z._changeContentWidth(0);

		Z.noRecordDiv = jqEl.find('.jl-tbl-norecord')[0];
		Z.noRecordDiv.style.top = Z.headSectionHt + 'px';
		Z.noRecordDiv.style.left = jqEl.find('.jl-tbl-fixedcol').width() + 5 + 'px';
		jqEl.find('.jl-tbl-vscroll-head').height(Z.headSectionHt + Z.fixedSectionHt);
		Z._renderBody();
	},

	_moveColumn: function(rowIndex, srcCellIndex, destCellIndex) {
		var Z = this;
		
		function moveOneRow(cells, srcStart, srcEnd, destStart, destEnd) {
			var cobj, 
				colNo = 0, 
				srcCells = [],
				destCells = [], i, len;
			
			for(i = 0, len = cells.length; i &lt; len; i++) {
				cobj = cells[i];
				if(colNo &gt;= srcStart &amp;&amp; colNo &lt;= srcEnd) {
					srcCells.push(cobj);
				} else if(colNo &gt;= destStart &amp;&amp; colNo &lt;= destEnd) {
					destCells.push(cobj);
				} else {
					if(colNo &gt; srcEnd &amp;&amp; colNo &gt; destEnd) {
						break;
					}
				}
				
				colNo += cobj.colSpan || 1;
			}
			var destCell;
			if(srcStart &gt; destStart) {	
				destCell = destCells[0];
				for(i = 0, len = srcCells.length; i &lt; len; i++) {
					jQuery(srcCells[i]).insertBefore(destCell);
				}
			} else {
				destCell = destCells[destCells.length - 1];
				for(i = srcCells.length; i &gt;= 0; i--) {
					jQuery(srcCells[i]).insertAfter(destCell);
				}
			}
		}
		
		function moveOneTableColumn(rows, rowIndex, srcStart, srcEnd, destStart, destEnd) {
			var rowCnt = rows.length;
			if(rowCnt === 0 || rowCnt === rowIndex) {
				return;
			}
			var rowObj, cellCnt;
			for(var i = rowIndex, len = rows.length; i &lt; len; i++) {
				rowObj = rows[i];
				moveOneRow(rowObj.cells, srcStart, srcEnd, destStart, destEnd);
			}
		}
		
		var rows = Z.rightHeadTbl.createTHead().rows, cobj,
			rowObj = rows[rowIndex],
			srcStart = 0,
			srcEnd = 0,
			destStart = 0,
			destEnd = 0, 
			preColNo = 0,
			colNo = 0;
		
		for(var i = 0, len = rowObj.cells.length; i &lt; len; i++) {
			cobj = rowObj.cells[i];
			preColNo = colNo; 
			colNo += (cobj.colSpan || 1);
			if(i === srcCellIndex) {
				srcStart = preColNo;
				srcEnd = colNo - 1;
			}
			if(i === destCellIndex) {
				destStart = preColNo;
				destEnd = colNo - 1;
			}
		}
		var srcCell = rowObj.cells[srcCellIndex],
			destCell = rowObj.cells[destCellIndex];
		var srcColCfg = srcCell.jsletColCfg,
			destColCfg = destCell.jsletColCfg,
			srcFldName = srcColCfg.field,
			destFldName = destColCfg.field;
		if(srcFldName &amp;&amp; destFldName) {
			Z._dataset.designMode(true);
			try {
				Z._dataset.moveField(srcFldName, destFldName);
			} finally {
				Z._dataset.designMode(false);
			}
		}
		var currField = Z.getFieldByColNum(Z._currColNum);
		var dataRows = Z.rightContentTbl.tBodies[0].rows;
		Z._changeColNum(dataRows[0], srcStart, srcEnd, destStart, destEnd);
		var headRows = Z.rightHeadTbl.createTHead().rows;
		moveOneTableColumn(headRows, rowIndex, srcStart, srcEnd, destStart, destEnd);
		moveOneTableColumn(Z.rightFixedTbl.tBodies[0].rows, 0, srcStart, srcEnd, destStart, destEnd);
		moveOneTableColumn(dataRows, 0, srcStart, srcEnd, destStart, destEnd);
		moveOneTableColumn(Z.rightFootTbl.tBodies[0].rows, 0, srcStart, srcEnd, destStart, destEnd);
		Z._dataset.selection.removeAll();
		Z._refreshSelection();
    	Z.gotoField(currField);
	},
	
	_changeColNum: function(rowObj, srcStart, srcEnd, destStart, destEnd) {
		if(!rowObj) {
			return;
		}
		var cobj, 
			colNo = 0, 
			srcCells = [],
			destCells = [],
			cells = rowObj.cells, i, len;
		
		for(i = 0, len = cells.length; i &lt; len; i++) {
			cobj = cells[i];
			if(colNo &gt;= srcStart &amp;&amp; colNo &lt;= srcEnd) {
				srcCells.push(cobj);
			} else if(colNo &gt;= destStart &amp;&amp; colNo &lt;= destEnd) {
				destCells.push(cobj);
			} else {
				if(colNo &gt; srcEnd &amp;&amp; colNo &gt; destEnd) {
					break;
				}
			}
			colNo += cobj.colSpan || 1;
		}
		var srcCellLen = srcCells.length,
			destCellLen = destCells.length,
			firstDestColNum= destCells[0].jsletColCfg.colNum,
			k = 0, colCfg;
		if(srcStart &gt; destStart) {
			for(i = srcStart; i &lt;= srcEnd; i++) {
				colCfg = cells[i].jsletColCfg;
				colCfg.colNum = firstDestColNum + (k++);
			}
			for(i = destStart; i &lt; srcStart; i++) {
				colCfg = cells[i].jsletColCfg;
				colCfg.colNum = colCfg.colNum + srcCellLen;
			}
		} else {
			var newStart = firstDestColNum - srcCellLen + destCellLen;
			for(i = srcStart; i &lt;= srcEnd; i++) {
				colCfg = cells[i].jsletColCfg;
				colCfg.colNum = newStart + (k++);
			}
			for(i = srcEnd + 1; i &lt;= destEnd; i++) {
				colCfg = cells[i].jsletColCfg;
				colCfg.colNum = colCfg.colNum - srcCellLen;
			}
		}		
	},
	
	_calcAndSetContentHeight: function() {
		var Z = this,
			jqEl = jQuery(Z.el);

		var rh = Z.rowHeight(), rows;
		if(Z._autoStretch) {
			rows = Z._dataset.recordCount() - Z._fixedRows;
			if(Z._oldVisibleRows === rows) {
				return;
			}
			if(Z._maxVisibleRows &gt; 0 &amp;&amp; rows &gt; Z._maxVisibleRows) {
				rows = Z._maxVisibleRows;
			}
			if(rows &lt; Z._minVisibleRows) {
				rows = Z._minVisibleRows;
			}
			Z.contentSectionHt = rh * rows + Z.footSectionHt;
			Z._repairHeight = 0;
			Z._oldVisibleRows = rows;
			Z.listvm.setVisibleCount(rows, true);
		} else {
			Z.contentSectionHt = Z.height - Z.headSectionHt - Z.fixedSectionHt;
			if (Z._isHoriOverflow) {
				Z.contentSectionHt -= jslet.ui.htmlclass.TABLECLASS.scrollBarWidth;
			}
			rows = Math.floor((Z.contentSectionHt - Z.footSectionHt) / rh);
			Z.listvm.setVisibleCount(rows, true);
			Z._repairHeight = Z.contentSectionHt - rows * rh;
		}
		
		//jqEl.find('.jl-tbl-contentcol').height(Z.height);
		jqEl.find('.jl-tbl-content-div').height(Z.contentSectionHt);

		Z.jqVScrollBar.height(Z.contentSectionHt);
		Z._setScrollBarMaxValue(Z.listvm.getNeedShowRowCount() * rh);
	},
	
	_stretchContentHeight: function() {
		if(!this._autoStretch) {
			return;
		}
		this._calcAndSetContentHeight();
		this._stretchContentRow();
	},

	_checkHoriOverflow: function() {
		var Z = this,
			contentWidth = Z.getTotalWidth();

		if(Z._hideHead) {
			Z._isHoriOverflow = contentWidth &gt; jQuery(Z.rightContentTbl.parentNode.parentNode).innerWidth();
		} else {
			Z._isHoriOverflow = contentWidth &gt; Z.rightHeadTbl.parentNode.parentNode.clientWidth;
		}
		Z._calcAndSetContentHeight();
	},
	
	_refreshHeadCell: function(fldName) {
		var Z = this,
			jqEl = jQuery(Z.el), oth = null, cobj;
		jqEl.find('.jl-tbl-header-cell').each(function(index, value) {
			cobj = this.jsletColCfg;
			if(cobj &amp;&amp; cobj.field == fldName) {
				oth = this;
				return false;
			}
		});
		if(!oth) {
			return;
		}
		var fldObj = Z._dataset.getField(cobj.field);
		cobj.label = fldObj.displayLabel();
		var ochild = oth.childNodes[0];
		var cellRender = cobj.cellRender || Z._defaultCellRender;
		if (cellRender &amp;&amp; cellRender.createHeader) {
			ochild.html('');
			cellRender.createHeader.call(Z, ochild, cobj);
		} else {
			var sh = cobj.label || '&amp;nbsp;';
			if(cobj.field &amp;&amp; Z._isCellEditable(cobj)) {
				if(fldObj &amp;&amp; fldObj.required()) {
					sh = '&lt;span class=&quot;jl-lbl-required&quot;&gt;*&lt;/span&gt;' + sh;
				}
			} 
			jQuery(oth).find('.jl-focusable-item').html(sh);
		}
	},
	
	_createHeadCell: function(otr, cobj) {
		var Z = this,
			rowSpan = 0, colSpan = 0;
		
		if (!cobj.subHeads) {
			rowSpan = Z.maxHeadRows - (cobj.level ? cobj.level: 0);
		} else {
			colSpan = cobj.colSpan;
		}
		var oth = document.createElement('th');
		oth.className = 'jl-tbl-header-cell jl-unselectable';
		oth.noWrap = true;
		oth.jsletColCfg = cobj;
		if (rowSpan &amp;&amp; rowSpan &gt; 1) {
			oth.rowSpan = rowSpan;
		}
		if (colSpan &amp;&amp; colSpan &gt; 1) {
			oth.colSpan = colSpan;
		}
		oth.innerHTML = '&lt;div style=&quot;position: relative&quot; unselectable=&quot;on&quot; class=&quot;jl-unselectable jl-tbl-header-div jl-border-box ' + 
			(cobj.widthCssName || '') +'&quot;&gt;';
		var ochild = oth.childNodes[0];
		var cellRender = cobj.cellRender || Z._defaultCellRender;
		if (cellRender &amp;&amp; cellRender.createHeader) {
			cellRender.createHeader.call(Z, ochild, cobj);
		} else {
			var sh = cobj.label || '&amp;nbsp;';
			if(cobj.field) {
				var fldObj = Z._dataset.getField(cobj.field);
				if(fldObj &amp;&amp; fldObj.required()) {
					sh = '&lt;span class=&quot;jl-lbl-required&quot;&gt;*&lt;/span&gt;' + sh;
				}
			} 
			ochild.innerHTML = [
			    Z._hasFilterDialog &amp;&amp; cobj.field &amp;&amp; !cobj.subHeads &amp;&amp; !cobj.disableFilter? '&lt;button class=&quot;jl-tbl-filter&quot; jsletfilterfield=&quot;' + cobj.field + 
			    		'&quot;&gt;&lt;i class=&quot;fa fa-filter&quot;&gt;&lt;/i&gt;&lt;/button&gt;': '',
			    '&lt;span id=&quot;',
				cobj.id, 
				'&quot; unselectable=&quot;on&quot; style=&quot;width:100%;padding:0px 2px&quot;&gt;',
				((!Z._disableHeadSort &amp;&amp; !cobj.disableHeadSort) ? '&lt;span class=&quot;jl-focusable-item&quot; draggable=&quot;true&quot;&gt;' + sh +'&lt;/span&gt;': sh),
				'&lt;/span&gt;&lt;span unselectable=&quot;on&quot; class=&quot;jl-tbl-sorter&quot; title=&quot;',
				jsletlocale.DBTable.sorttitle,
				'&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;div  unselectable=&quot;on&quot; class=&quot;jl-tbl-splitter-hook&quot; colid=&quot;',
				cobj.colNum,
				'&quot;&gt;&amp;nbsp;&lt;/div&gt;'].join('');
		}
		otr.appendChild(oth);	
	}, // end _createHeadCell

	_renderHeader: function() {
		var Z = this;
		if (Z._hideHead) {
			return;
		}
		var otr, otrLeft = null, cobj, otrRight, otd, oth, i, cnt,
			leftHeadObj = Z.leftHeadTbl.createTHead(),
			rightHeadObj = Z.rightHeadTbl.createTHead();
		for(i = 0; i &lt; Z.maxHeadRows; i++) {
			leftHeadObj.insertRow(-1);
			rightHeadObj.insertRow(-1);
		}
		otr = leftHeadObj.rows[0];
		for(i = 0, cnt = Z._sysColumns.length; i &lt; cnt; i++) {
			cobj = Z._sysColumns[i];
			cobj.rowSpan = Z.maxHeadRows;
			Z._createHeadCell(otr, cobj);
		}
		function travHead(arrHeadCfg) {
			var cobj, otr;
			for(var m = 0, ccnt = arrHeadCfg.length; m &lt; ccnt; m++) {
				cobj = arrHeadCfg[m];
				if (cobj.colNum &lt; Z._fixedCols) {
					otr = leftHeadObj.rows[cobj.level];
				} else {
					otr = rightHeadObj.rows[cobj.level];
				}
				Z._createHeadCell(otr, cobj);
				if (cobj.subHeads) {
					travHead(cobj.subHeads);
				}
			}
		}
		travHead(Z.innerHeads);
		var jqTr1, jqTr2, h= Z.headRowHeight();
		for(i = 0; i &lt;= Z.maxHeadRows; i++) {
			jqTr1 = jQuery(leftHeadObj.rows[i]);
			jqTr2 = jQuery(rightHeadObj.rows[i]);
			jqTr1.height(h);
			jqTr2.height(h);
		}
		Z.sortedCell = null;
		Z.indexField = null;
	}, // end renderHead

	getTotalWidth: function(isLeft) {
		var Z= this,
			totalWidth = 0, i, cnt;
		if(!isLeft) {
			for(i = Z._fixedCols, cnt = Z.innerColumns.length; i &lt; cnt; i++) {
				totalWidth += Z.innerColumns[i].width;
			}
		} else {
			for(i = 0, cnt = Z._sysColumns.length; i &lt; cnt; i++) {
				totalWidth += Z._sysColumns[i].width;
			}
			for(i = 0, cnt = Z._fixedCols; i &lt; cnt; i++) {
				totalWidth += Z.innerColumns[i].width;
			}
		}
		return totalWidth;
	},
	
	_doSplitHookDown: function(event) {
		event = jQuery.event.fix( event || window.event );
		var ohook = event.target;
		if (ohook.className != 'jl-tbl-splitter-hook') {
			return;
		}
		var tblContainer = jslet.ui.findFirstParent(ohook, function(el) {
			return el.tagName.toLowerCase() == 'div' &amp;&amp; el.jslet &amp;&amp; el.jslet._dataset;
		});
		var jqTblContainer = jQuery(tblContainer),
			jqBody = jQuery(document.body), 
			bodyMTop = parseInt(jqBody.css('margin-top')),
			bodyMleft = parseInt(jqBody.css('margin-left')),
			y = jqTblContainer.position().top - bodyMTop,
			jqHook = jQuery(ohook),
			h = jqTblContainer.height() - 20,
			currLeft = jqHook.offset().left - jqTblContainer.offset().left - bodyMleft,
			splitDiv = jqTblContainer.find('.jl-tbl-splitter')[0];
		splitDiv.style.left = currLeft + 'px';
		splitDiv.style.top = '1px';
		splitDiv.style.height = h + 'px';
		jslet.ui.dnd.bindControl(splitDiv);
		tblContainer.jslet.currColId = parseInt(jqHook.attr('colid'));
		tblContainer.jslet.isDraggingColumn = true;
	},

	_doSplitHookUp: function(event) {
		event = jQuery.event.fix( event || window.event );
		var ohook = event.target.lastChild;
		if (!ohook || ohook.className != 'jl-tbl-splitter-hook') {
			return;
		}
		var tblContainer = jslet.ui.findFirstParent(ohook, function(el) {
			return el.tagName.toLowerCase() == 'div' &amp;&amp; el.jslet &amp;&amp; el.jslet._dataset;
		});

		var jqTblContainer = jQuery(tblContainer),
			jqBody = jQuery(document.body); 
		jqBody.css('cursor','auto');

		var splitDiv = jqTblContainer.find('.jl-tbl-splitter')[0];
		if (splitDiv.style.display != 'none') {
			splitDiv.style.display = 'none';
		}
		tblContainer.jslet.isDraggingColumn = false;
	},

	_getColumnByField: function(fieldName) {
		var Z = this;
		if (!Z.innerColumns) {
			return null;
		}
		var cobj;
		for (var i = 0, cnt = Z.innerColumns.length; i &lt; cnt; i++) {
			cobj = Z.innerColumns[i];
			if (cobj.field == fieldName) {
				return cobj;
			}
		}
		return null;
	},

	_doHeadClick: function(colCfg, ctrlKeyPressed) {
		var Z = this;
		if (Z._disableHeadSort || colCfg.disableHeadSort || Z.isDraggingColumn) {
			return;
		}
		Z._doSort(colCfg.field, ctrlKeyPressed);
	}, // end _doHeadClick

	_doSort: function(sortField, isMultiSort) {
		var Z = this;
		Z._dataset.confirm();
		Z._dataset.disableControls();
		try {
			Z._dataset.toggleIndexField(sortField, !isMultiSort);
			Z.listvm.refreshModel();
		} finally {
			Z._dataset.enableControls();
		}
	},

	_updateSortFlag: function() {
		var Z = this;
		if (Z._hideHead) {
			return;
		}

		var sortFields = Z._dataset.mergedIndexFields();
		
		var leftHeadObj = Z.leftHeadTbl.createTHead(),
			rightHeadObj = Z.rightHeadTbl.createTHead(),
			leftHeadCells, rightHeadCells,
			allHeadCells = [], oth, i, cnt, r,
			rowCnt = leftHeadObj.rows.length;
		for(r = 0; r &lt; rowCnt; r++) {
			leftHeadCells = leftHeadObj.rows[r].cells;
			for (i = 0, cnt = leftHeadCells.length; i &lt; cnt; i++) {
				oth = leftHeadCells[i];
				if (oth.jsletColCfg) {
					allHeadCells[allHeadCells.length] = oth;
				}
			}
		}
		for(r = 0; r &lt; rowCnt; r++) {
			rightHeadCells =  rightHeadObj.rows[r].cells;
			for (i = 0, cnt = rightHeadCells.length; i &lt; cnt; i++) {
				oth = rightHeadCells[i];
				if (oth.jsletColCfg) {
					allHeadCells[allHeadCells.length] = oth;
				}
			}
		}
		var sortDiv, 
			cellCnt = allHeadCells.length;
		for (i = 0; i &lt; cellCnt; i++) {
			oth = allHeadCells[i];
			sortDiv = jQuery(oth).find('.jl-tbl-sorter')[0];
			if (sortDiv) {
				sortDiv.innerHTML = '&amp;nbsp;';
			}
		}
		var fldName, sortFlag, sortObj, 
			k = 1,
			len = sortFields.length;
		for (i = 0; i &lt; len; i++) {
			sortObj = sortFields[i];
			for (var j = 0; j &lt; cellCnt; j++) {
				oth = allHeadCells[j];
				fldName = oth.jsletColCfg.field;
				if (!fldName) {
					continue;
				}
				sortDiv = jQuery(oth).find('.jl-tbl-sorter')[0];
				sortFlag = '&amp;nbsp;';
				if (fldName == sortObj.fieldName) {
					sortFlag = sortObj.order === 1 ? '&lt;i class=&quot;fa fa-arrow-up&quot;&gt;&lt;/i&gt;' : '&lt;i class=&quot;fa fa-arrow-down&quot;&gt;&lt;/i&gt;';
					if (len &gt; 1) {
						sortFlag += k++;
					}
					sortDiv.innerHTML = sortFlag;
					break;
				}
			}
		}
	},

	_doSelectBoxClick: function(event) {
		var ocheck = null;
		if (event) {
			event = jQuery.event.fix( event || window.event );
			ocheck = event.target;
		} else {
			ocheck = this;
		}
		var otr = jslet.ui.getParentElement(ocheck, 2);
		try {
			jQuery(otr).click();// tr click
		} finally {
			var otable = jslet.ui.findFirstParent(otr, function(node) {
				return node.jslet? true: false;
			});
			var oJslet = otable.jslet;

			if (oJslet._onSelect) {
				var flag = oJslet._onSelect.call(oJslet, ocheck.checked);
				if (flag !== undefined &amp;&amp; !flag) {
					ocheck.checked = !ocheck.checked;
					return;
				}
			}
			if(oJslet._beforeSelect) {
				oJslet._beforeSelect.call(oJslet);
			}
			oJslet._dataset.select(ocheck.checked ? 1 : 0, oJslet._selectBy);
			oJslet._showCurrentCell();
			if(oJslet._afterSelect) {
				oJslet._afterSelect.call(oJslet);
			}
		}
	}, // end _doSelectBoxClick

	_getTableRow: function(otd, offsetTop) {
		var otr = otd.parentElement,
			rowSpan = otd.rowSpan;
		if(rowSpan &lt; 2) {
			return otr;
		}
		var currIdx = otr.rowIndex;
		var rows = otr.parentElement.rows;
		var height = 0;
		for(var i = currIdx; i &lt; currIdx + rowSpan; i++) {
			otr = rows[i];
			height += otr.offsetHeight;
			if(height &gt;= offsetTop) {
				return otr;
			}
		}
		return otr;
	},
	
	_doCellClick: function(otd, offsetTop, event) {
		var Z = this;
    	var colCfg = otd.jsletColCfg;
    	if(!colCfg) {
    		return;
    	}
    	Z._doRowClick(Z._getTableRow(otd, offsetTop));
		if(colCfg.isSeqCol) { //If the cell is sequence cell, process row selection.
			Z._processRowSelection(event.ctrlKey, event.shiftKey, event.altKey);
		} else {
    		var colNum = colCfg.colNum;
    		if(colNum !== 0 &amp;&amp; !colNum) {
    			return;
    		}
			Z._doBeforeSelect(event.ctrlKey, event.shiftKey, event.altKey);
    		Z.currColNum(colNum);
			Z._processSelection(event.ctrlKey, event.shiftKey, event.altKey);
	    	var cellEditor = Z.cellEditor();
	    	if(cellEditor) {
	    		if(Z._isCellEditable(colCfg)) {
	    			var fldName = colCfg.field;
	    			cellEditor.showEditor(fldName, otd);
	    			if(colCfg.isBoolColumn) {
	    				window.setTimeout(function() {
	        				Z._dataset.setFieldValue(fldName, !Z._dataset.getFieldValue(fldName));
	    				}, 5);
	    			}
	    		} else {
	    			cellEditor.hideEditor();
	    		}
	    	}
		}
		if (Z._onCellClick) {
			Z._onCellClick.call(Z, otd);
		}
	},
	
	_doDblCellClick: function(otd, offsetTop) {
    	this._doRowDblClick(this._getTableRow(otd, offsetTop));
	},

	_doRowDblClick: function(otr) {
		if(!otr) {
			return;
		}
		if (this._onRowDblClick) {
			this._onRowDblClick.call(this, otr);
		}
	},

	_doRowClick: function(otr) {
		if(!otr) {
			return;
		}
		var Z = this,
			dsObj = Z.dataset(),
			otrRecno = otr.jsletrecno; 
		if(otrRecno !== dsObj.recno()) {
			if(dsObj.status()) {
				dsObj.confirm();
			}
	
			var rowno = Z.listvm.recnoToRowno(otrRecno);
			Z.listvm.setCurrentRowno(rowno);
			dsObj.recno(Z.listvm.getCurrentRecno());
		}
		if (Z._onRowClick) {
			Z._onRowClick.call(Z, otr);
		}
	},

	_renderCell: function(otr, colCfg, isFirstCol) {
		var Z = this;
		var otd = document.createElement('td');
		otd.noWrap = true;
		otd.jsletColCfg = colCfg;
		jQuery(otd).addClass('jl-tbl-cell');
		otd.innerHTML = '&lt;div class=&quot;jl-tbl-cell-div ' + (colCfg.widthCssName || '') + '&quot;&gt;&lt;/div&gt;';
		var ochild = otd.firstChild;
		var cellRender = colCfg.cellRender || Z._defaultCellRender;
		if (cellRender &amp;&amp; cellRender.createCell) {
			cellRender.createCell.call(Z, ochild, colCfg);
		} else if (!Z._isCellEditable(colCfg)) {
				jslet.ui.table.cellRenders.defaultCellRender.createCell.call(Z, ochild, colCfg);
				colCfg.editable = false;
		} else {
			jslet.ui.table.cellRenders.defaultCellRender.createCell.call(Z, ochild, colCfg);
			colCfg.editable = true;
		}
		otr.appendChild(otd);
	},

	_renderRow: function(sectionNum, onlyRefreshContent) {
		var Z = this;
		var rowCnt = 0, leftBody = null, rightBody,
			hasLeft = Z._fixedCols &gt; 0 || Z._sysColumns.length &gt; 0;
		switch (sectionNum) {
			case 1:
				{//fixed data
					rowCnt = Z._fixedRows;
					if (hasLeft) {
						leftBody = Z.leftFixedTbl.tBodies[0];
					}
					rightBody = Z.rightFixedTbl.tBodies[0];
					break;
				}
			case 2:
				{//data content
					rowCnt = Z.listvm.getVisibleCount();
					if (hasLeft) {
						leftBody = Z.leftContentTbl.tBodies[0];
					}
					rightBody = Z.rightContentTbl.tBodies[0];
					break;
				}
		}
		var otr, oth, colCfg, isfirstCol, 
			startRow = 0, j,
			fldCnt = Z.innerColumns.length;
		if (onlyRefreshContent) {
			startRow = rightBody.rows.length;
		}
		var rh = Z.rowHeight();
		// create date content table row
		for (var i = startRow; i &lt; rowCnt; i++) {
			if (hasLeft) {
				otr = leftBody.insertRow(-1);
				otr.style.height = rh + 'px';

				var sysColLen = Z._sysColumns.length;
				for(j = 0; j &lt; sysColLen; j++) {
					colCfg = Z._sysColumns[j];
					Z._renderCell(otr, colCfg, j === 0);
				}
				
				isfirstCol = sysColLen === 0;
				for(j = 0; j &lt; Z._fixedCols; j++) {
					colCfg = Z.innerColumns[j];
					Z._renderCell(otr, colCfg, isfirstCol &amp;&amp; j === 0);
				}
			}
			isfirstCol = !hasLeft;
			otr = rightBody.insertRow(-1);
			otr.style.height = rh + 'px';
			for (j = Z._fixedCols; j &lt; fldCnt; j++) {
				colCfg = Z.innerColumns[j];
				Z._renderCell(otr, colCfg, j == Z._fixedCols);
			}
		}
	},

	_stretchContentRow: function() {
		var Z = this;
		var visibleRowCnt = Z.listvm.getVisibleCount() - Z._fixedRows, 
			leftBody = null, leftRows = null,
			rightBody, rightRows,
			hasLeft = Z._fixedCols &gt; 0 || Z._sysColumns.length &gt; 0;

		if (hasLeft) {
			leftBody = Z.leftContentTbl.tBodies[0];
			leftRows = leftBody.rows;
		}
		rightBody = Z.rightContentTbl.tBodies[0];
		rightRows = rightBody.rows;
		var i, oldRowCount = rightRows.length;
		//Hide the extra rows
		for(i = visibleRowCnt; i &lt; oldRowCount; i++) {
			if(hasLeft) {
				leftRows[i].style.display = 'none';
			}
			rightRows[i].style.display = 'none';
		}
		
		var otr, colCfg, isfirstCol, j,
			fldCnt = Z.innerColumns.length,
			rh = Z.rowHeight();
		// create date content table row
		for (i = 0; i &lt; visibleRowCnt; i++) {
			if (i &lt; oldRowCount) {
				if(hasLeft) {
					leftRows[i].style.display = 'table-row';
				}
				rightRows[i].style.display = 'table-row';
				continue;
			}
			if (hasLeft) {
				otr = leftBody.insertRow(-1);
				otr.style.height = rh + 'px';

				var sysColLen = Z._sysColumns.length;
				for(j = 0; j &lt; sysColLen; j++) {
					colCfg = Z._sysColumns[j];
					Z._renderCell(otr, colCfg, j === 0);
				}
				
				isfirstCol = sysColLen === 0;
				for(j = 0; j &lt; Z._fixedCols; j++) {
					colCfg = Z.innerColumns[j];
					Z._renderCell(otr, colCfg, isfirstCol &amp;&amp; j === 0);
				}
			}
			isfirstCol = !hasLeft;
			otr = rightBody.insertRow(-1);
			otr.style.height = rh + 'px';
			for (j = Z._fixedCols; j &lt; fldCnt; j++) {
				colCfg = Z.innerColumns[j];
				Z._renderCell(otr, colCfg, j == Z._fixedCols);
			}
		}
	},
	
	_renderBody: function(onlyRefreshContent) {
		var Z = this;
		if (onlyRefreshContent) {
			Z._renderRow(2, true);
		} else {
			Z._renderRow(1);
			Z._renderRow(2);
			Z._renderTotalSection();
		}
	}, // end _renderBody

	_renderTotalSection: function() {
		var Z = this;
		if (!Z.footSectionHt) {
			return;
		}
		var hasLeft = Z._fixedCols &gt; 0 || Z._sysColumns.length &gt; 0,
			leftBody,
			rightBody,
			otr, colCfg, j, len;
		if (hasLeft) {
			leftBody = Z.leftFootTbl.tBodies[0];
		}
		rightBody = Z.rightFootTbl.tBodies[0];
		
		function createCell(colCfg) {
			var otd = document.createElement('td');
			otd.noWrap = true;
			otd.innerHTML = '&lt;div class=&quot;jl-tbl-footer-div ' + (colCfg.widthCssName || '') + '&quot;&gt;&lt;/div&gt;';
			otd.jsletColCfg = colCfg;
			return otd;
		}
		
		if (hasLeft) {
			otr = leftBody.insertRow(-1);
			otr.style.height = Z.footerRowHeight() + 'px';

			for(j = 0, len = Z._sysColumns.length; j &lt; len; j++) {
				colCfg = Z._sysColumns[j];
				otr.appendChild(createCell(colCfg));
			}
			
			for(j = 0; j &lt; Z._fixedCols; j++) {
				colCfg = Z.innerColumns[j];
				otr.appendChild(createCell(colCfg));
			}
		}
		otr = rightBody.insertRow(-1);
		otr.style.height = Z.footerRowHeight() + 'px';
		for (j = Z._fixedCols, len = Z.innerColumns.length; j &lt; len; j++) {
			colCfg = Z.innerColumns[j];
			otr.appendChild(createCell(colCfg));
		}
	},
	
	_fillTotalSection: function() {
		var Z = this,
			aggregateValues = Z._dataset.aggregatedValues();
		if (!Z.footSectionHt || !aggregateValues) {
			return;
		}
		var sysColCnt = Z._sysColumns.length,
			hasLeft = Z._fixedCols &gt; 0 || sysColCnt &gt; 0,
			otrLeft, otrRight;
		if (hasLeft) {
			otrLeft = Z.leftFootTbl.tBodies[0].rows[0];
		}
		otrRight = Z.rightFootTbl.tBodies[0].rows[0];

		var otd, k = 0, fldObj, cobj, fldName, totalValue;
		var aggregateValueObj,
			labelDisplayed = false,
			canShowLabel = true;
		if(sysColCnt &gt; 0) {
			otd = otrLeft.cells[sysColCnt - 1];
			otd.innerHTML = jsletlocale.DBTable.totalLabel;
			canShowLabel = false;
		}
		var colNum;
		for (var i = 0, len = Z.innerColumns.length; i &lt; len; i++) {
			cobj = Z.innerColumns[i];
			colNum = cobj.colNum;
			if (colNum &lt; Z._fixedCols) {
				otd = otrLeft.cells[colNum + sysColCnt];
			} else {
				otd = otrRight.cells[colNum - Z._fixedCols];
			}
			otd.style.textAlign = 'right';

			fldName = cobj.field;
			aggregateValueObj = aggregateValues[fldName];
			if (!aggregateValueObj) {
				if(canShowLabel) {
					var content;
					if(labelDisplayed) {
						content = '&amp;nbsp;';
					} else {
						content = jsletlocale.DBTable.totalLabel;
						labelDisplayed = true;
					}
					otd.firstChild.innerHTML = content;
					otd.firstChild.title = '';
				}
				continue;
			}
			canShowLabel = false;
			fldObj = Z._dataset.getField(fldName);
			if(fldObj.getType() === jslet.data.DataType.NUMBER) {
				totalValue = aggregateValueObj.sum;
			} else {
				totalValue = aggregateValueObj.count;
			}
			if(!totalValue) {
				otd.firstChild.innerHTML = '&amp;nbsp;';
				otd.firstChild.title = '';
				continue;
			}
			var dispFmt = fldObj.displayFormat();
			var displayValue = totalValue;
			if(dispFmt &amp;&amp; fldObj.getType() === jslet.data.DataType.NUMBER) {
				displayValue = totalValue? jslet.formatNumber(totalValue, dispFmt) : '';
			}
			otd.firstChild.innerHTML = displayValue;
			otd.firstChild.title = displayValue;
		}
	},
	
	_fillData: function() {
		var Z = this;
		var preRecno = Z._dataset.recno(),
			allCnt = Z.listvm.getNeedShowRowCount(),
			h = allCnt * Z.rowHeight();
		Z._setScrollBarMaxValue(h);
		var noRecord = Z.dataset().recordCount() === 0;
		Z.noRecordDiv.style.display = (noRecord ?'block':'none');
		var oldRecno = Z._dataset.recnoSilence();
		try {
			Z._fillRow(true);
			Z._fillRow(false);
			if (Z.footSectionHt) {
				Z._fillTotalSection();
			}
		} finally {
			Z._dataset.recnoSilence(oldRecno);
		}
		Z._refreshSeqColWidth();
		if(Z._cellEditor &amp;&amp; noRecord) {
			Z._cellEditor.hideEditor();
		}
	},

	_fillRow: function(isFixed) {
		var Z = this,
			rowCnt = 0, start = 0, leftBody = null, rightBody,
			hasLeft = Z._fixedCols &gt; 0 || Z._sysColumns.length &gt; 0;
			
		if (isFixed) {
			rowCnt = Z._fixedRows;
			start = -1 * Z._fixedRows;
			if (rowCnt === 0) {
				return;
			}
			if (hasLeft) {
				leftBody = Z.leftFixedTbl.tBodies[0];
			}
			rightBody = Z.rightFixedTbl.tBodies[0];
		} else {
			rowCnt = Z.listvm.getVisibleCount();
			start = Z.listvm.getVisibleStartRow();
			if (hasLeft) {
				leftBody = Z.leftContentTbl.tBodies[0];
			}
			rightBody = Z.rightContentTbl.tBodies[0];
		}
		
		var otr, colCfg, isfirstCol, recNo = -1, cells, clen, otd, j, 
			fldCnt = Z.innerColumns.length,
			allCnt = Z.listvm.getNeedShowRowCount() - Z.listvm.getVisibleStartRow(),
			fixedRows = hasLeft ? leftBody.rows : null,
			contentRows = rightBody.rows,
			sameValueNodes = {},
			isFirst = true,
			actualCnt = Math.min(contentRows.length, rowCnt);

		for (var i = 0; i &lt; actualCnt ; i++) {
			if (i + (isFixed? start: 0) &gt;= allCnt) {
				if (hasLeft) {
					otr = fixedRows[i];
					otr.style.display = 'none';
				}
				otr = contentRows[i];
				otr.style.display = 'none';
				continue;
			}

			Z.listvm.setCurrentRowno(i + start, true);
			recNo = Z.listvm.getCurrentRecno();
			Z._dataset.recnoSilence(recNo);
			if (hasLeft) {
				otr = fixedRows[i];
				otr.jsletrecno = recNo;
				otr.style.display = '';
				if (Z._onFillRow) {
					Z._onFillRow.call(Z, otr, Z._dataset);
				}
				cells = otr.childNodes;
				clen = cells.length;
				for (j = 0; j &lt; clen; j++) {
					otd = cells[j];
					Z._fillCell(recNo, otd, sameValueNodes, isFirst);
				}
			}

			otr = contentRows[i];
			otr.jsletrecno = recNo;
			otr.style.display = '';
			if (Z._onFillRow) {
				Z._onFillRow.call(Z, otr, Z._dataset);
			}
			// fill content table
			otr = contentRows[i];
			cells = otr.childNodes;
			clen = cells.length;
			for (j = 0; j &lt; clen; j++) {
				otd = cells[j];
				Z._fillCell(recNo, otd, sameValueNodes, isFirst);
			} //end for data content field
			isFirst = 0;
		} //end for records
		Z.listvm.setCurrentRowno(0, true);
	},

	_fillCell: function(recNo, otd, sameValueNodes, isFirst) {
		var Z = this,
			colCfg = otd.jsletColCfg;
		if (!colCfg) {
			return;
		}
		var fldName = colCfg.field,
			cellPanel = otd.firstChild,
			jqTd;
		
		if (Z._onFillCell) {
			Z._onFillCell.call(Z, cellPanel, Z._dataset, fldName);
		}
		if (fldName &amp;&amp; colCfg.mergeSame &amp;&amp; sameValueNodes) {
			if (isFirst || !Z._dataset.isSameAsPrevious(fldName)) {
				sameValueNodes[fldName] = { cell: otd, count: 1 };
				jqTd = jQuery(otd);
				jqTd.attr('rowspan', 1);
				if(Z._editable) {
					var offset = jqTd.offset();
					jqTd.attr('jsletFirstId', null);
					if(!otd.id) {
						otd.id = jslet.nextId();
					}
				}
				otd.style.display = '';
			}
			else {
				var sameNode = sameValueNodes[fldName];
				sameNode.count++;
				otd.style.display = 'none';
				var jqFirstTd = jQuery(sameNode.cell);
				if(Z._editable) {
					jQuery(otd).attr('jsletOffsetHeight', (sameNode.count -1) * Z.rowHeight()).attr('jsletFirstId', jqFirstTd.attr('id'));
				}
				jqFirstTd.attr('rowspan', sameNode.count);
			}
		}
		var cellRender = colCfg.cellRender || Z._defaultCellRender;
		if (cellRender &amp;&amp; cellRender.refreshCell) {
			cellRender.refreshCell.call(Z, cellPanel, colCfg, recNo);
		} else {
			jslet.ui.table.cellRenders.defaultCellRender.refreshCell.call(Z, cellPanel, colCfg, recNo);
		}
		if(fldName) {
			var errObj = Z._dataset.getFieldErrorByRecno(recNo, fldName);
			jqTd = jQuery(otd);
			var title = cellPanel.title;
			if(errObj &amp;&amp; errObj.message) {
				if(!jqTd.hasClass('has-error')) {
					jqTd.addClass('has-error');
				}
				cellPanel.title = errObj.message;
			} else {
				if(jqTd.hasClass('has-error')) {
					jqTd.removeClass('has-error');
				}
			}
		}
	},

	refreshCurrentRow: function() {
		var Z = this,
			hasLeft = Z._fixedCols &gt; 0 || Z._hasSeqCol || Z._hasSelectCol,
			fixedBody = null, contentBody, idx,
			recno = Z._dataset.recno();

		if (recno &lt; Z._fixedRows) {
			if (hasLeft) {
				fixedBody = Z.leftFixedTbl.tBodies[0];
			}
			contentBody = Z.rightFixedTbl.tBodies[0];
			idx = recno;
		}
		else {
			if (hasLeft) {
				fixedBody = Z.leftContentTbl.tBodies[0];
			}
			contentBody = Z.rightContentTbl.tBodies[0];
			idx = Z.listvm.recnoToRowno(Z._dataset.recno()) - Z.listvm.getVisibleStartRow();
		}

		var otr, cells, otd, recNo, colCfg, j, clen;

		if (hasLeft) {
			otr = fixedBody.rows[idx];
			if (!otr) {
				return;
			}
			cells = otr.childNodes;
			recNo = otr.jsletrecno;
			if (Z._onFillRow) {
				Z._onFillRow.call(Z, otr, Z._dataset);
			}
			var ocheck;
			for (j = 0, clen = cells.length; j &lt; clen; j++) {
				otd = cells[j];
				colCfg = otd.jsletColCfg;
				if (colCfg &amp;&amp; colCfg.isSeqCol) {
					colCfg.cellRender.refreshCell.call(Z, otd.firstChild, colCfg);
					continue;
				}
				if (colCfg &amp;&amp; colCfg.isSelectCol) {
					ocheck = otd.firstChild;
					ocheck.checked = Z._dataset.selected();
					continue;
				}
				Z._fillCell(recNo, otd);
			}
		}

		otr = contentBody.rows[idx];
		if (!otr) {
			return;
		}
		recNo = otr.jsletrecno;
		if (Z._onFillRow) {
			Z._onFillRow.call(Z, otr, Z._dataset);
		}
		// fill content table
		cells = otr.childNodes;
		for (j = 0, clen = cells.length; j &lt; clen; j++) {
			otd = cells[j];
			Z._fillCell(recNo, otd);
		}
	},

	_getLeftRowByRecno: function(recno) {
		var Z = this;
		if (recno &lt; Z._fixedRows) {
			return Z.leftFixedTbl.tBodies[0].rows[recno];
		}
		var rows = Z.leftContentTbl.tBodies[0].rows, row;
		for (var i = 0, cnt = rows.length; i &lt; cnt; i++) {
			row = rows[i];
			if (row.jsletrecno == recno) {
				return row;
			}
		}
		return null;
	}, // end _getLeftRowByRecno

	_showCurrentRow: function(checkVisible) {//Check if current row is in visible area
		var Z = this,
			rowno = Z.listvm.recnoToRowno(Z._dataset.recno());
		Z.listvm.setCurrentRowno(rowno, false, checkVisible);
		Z._showCurrentCell();
	},

	_hideCurrentRow: function() {
		var Z = this;
		if (Z._currRow) {
			if (Z._currRow.fixed) {
				jQuery(Z._currRow.fixed).removeClass(jslet.ui.htmlclass.TABLECLASS.currentrow);
			}
			jQuery(Z._currRow.content).removeClass(jslet.ui.htmlclass.TABLECLASS.currentrow);
		}
		
	},
	
	_getTrByRowno: function(rowno) {
		var Z = this, 
			hasLeft = Z._fixedCols &gt; 0 || Z._sysColumns.length &gt; 0,
			idx, otr, k, rows, row, fixedRow;

		if (rowno &lt; 0) {//fixed rows
			rows = Z.rightFixedTbl.tBodies[0].rows;
			k = Z._fixedRows + rowno;
			row = rows[k];
			fixedRow = (hasLeft ? Z.leftFixedTbl.tBodies[0].rows[k] : null);
			return { fixed: fixedRow, content: row };
		}
		//data content
		rows = Z.rightContentTbl.tBodies[0].rows;
		k = rowno - Z.listvm.getVisibleStartRow();
		if (k &gt;= 0) {
			row = rows[k];
			if (!row) {
				return null;
			}
			fixedRow = hasLeft ? Z.leftContentTbl.tBodies[0].rows[k] : null;
			return { fixed: fixedRow, content: row };
		}
		return null;
	},

	_getCurrCellEl: function() {
		var Z = this;
		if(!Z._currRow) {
			return null;
		}
		var contentRow = Z._currRow.content,
			cells = contentRow.cells, 
			cellEl, colCfg;
		for(var i = 0, len = cells.length; i &lt; len; i++) {
			cellEl = cells[i];
			colCfg = cellEl.jsletColCfg;
			if(colCfg &amp;&amp; colCfg.colNum === Z._currColNum) {
				return cellEl;
			}
		}
		return null;
	},
	
	_adjustCurrentCellPos: function(isLeft) {
		var Z = this;
		if(!Z._currRow) {
			return;
		}
		var jqEl = jQuery(Z.el),
			jqContentPanel = jqEl.find('.jl-tbl-contentcol'),
			contentPanel = jqContentPanel[0],
			oldScrLeft = contentPanel.scrollLeft;
		if(Z._currColNum &lt; Z._fixedCols) { //If current cell is in fixed content area
			return;
		}
		var currTd = Z._getCurrCellEl(),
			currTdLeft = currTd.offsetLeft,
			currTdWidth = currTd.offsetWidth,
			containerWidth = contentPanel.clientWidth,
			containerLeft = contentPanel.scrollLeft;
		if(currTdLeft &lt; containerLeft) {
			contentPanel.scrollLeft = currTdLeft;
			return;
		}
		if(currTdLeft + currTdWidth &gt; containerLeft + containerWidth) {
			contentPanel.scrollLeft = currTdLeft + currTdWidth - containerWidth;
		}
	},

	_isCurrCellInView: function() {
		var Z = this,
			jqEl = jQuery(Z.el),
			jqContentPanel = jqEl.find('.jl-tbl-contentcol'),
			contentPanel = jqContentPanel[0],
			borderW = (Z._noborder ? 0: 2),
			oldScrLeft = contentPanel.scrollLeft,
			currColLeft = 0, i, len;
		if(Z._currColNum &lt; Z._fixedCols) { //If current cell is in fixed content area
			return true;
		}
		for(i = Z._fixedCols, len = Z.innerColumns.length; i &lt; Z._currColNum; i++) {
			currColLeft += (Z.innerColumns[i].width + borderW); //&quot;2&quot; is the cell border's width(left+right)
		}
		if(currColLeft &lt; oldScrLeft) {
			return false; 
		}
		var containerWidth = jqContentPanel.innerWidth(),
			contentWidth = jqContentPanel.find('.jl-tbl-content-div').width(),
			scrWidth = 0;
		for(i = Z.innerColumns.length - 1; i &gt; Z._currColNum; i--) {
			scrWidth += (Z.innerColumns[i].width + borderW); //&quot;2&quot; is the cell border's width(left+right)
		}
		currColLeft = contentWidth - scrWidth - containerWidth;
		currColLeft = (currColLeft &gt;= 0? currColLeft: 0);
		if(currColLeft &gt; oldScrLeft) {
			return false; 
		}
		
		return true;
	},
	
	_showCurrentCell: function() {
		var Z = this,
			rowObj = Z._currRow;
		if(!rowObj) {
			return;
		}
		var otr;
		if(Z._currColNum &gt;= Z._fixedCols) {
			otr = rowObj.content;
		} else {
			otr = rowObj.fixed;
		}
		var recno = otr.jsletrecno;
    	var cellEditor = Z.cellEditor();
		if(recno !== Z._dataset.recno()) {
    		if(Z.prevCell) {
    			Z.prevCell.removeClass('jl-tbl-curr-cell');
    		}
        	if(cellEditor) {
       			cellEditor.hideEditor();
        	}
			return;
		}
		var ocells = otr.cells, otd, colCfg, found = false;
		for(var i = 0, len = ocells.length; i &lt; len; i++) {
			otd = ocells[i];
        	colCfg = otd.jsletColCfg;
        	if(colCfg &amp;&amp; colCfg.colNum == Z._currColNum) {
        		if(Z.prevCell) {
        			Z.prevCell.removeClass('jl-tbl-curr-cell');
        		}
        		var jqCell = jQuery(otd);
        		jqCell.addClass('jl-tbl-curr-cell');
        		Z.prevCell = jqCell;
        		found = true;
        		break;
        	}
		}
    	if(cellEditor &amp;&amp; found) {
    		if(Z._isCurrCellInView()) {
    			cellEditor.showEditor(colCfg.field, otd);
    		} else {
    			cellEditor.hideEditor();
    		}
    	}
	},
	
	_showSelected: function(otd, fldName, recno) {
		var Z = this,
			jqCell = jQuery(otd);
		if(recno === undefined) {
			recno = Z._dataset.recno();
		}
		var isSelected = Z._dataset.selection.isSelected(recno, fldName);
		if(isSelected) {
			jqCell.addClass('jl-tbl-selected');
		} else {
			jqCell.removeClass('jl-tbl-selected');
		}
	},
	
	_refreshSelection: function() {
		var Z = this;
		jQuery(Z.el).find('td.jl-tbl-cell').each(function(k, otd) {
        	var colCfg = otd.jsletColCfg;
        	var recno = parseInt(otd.parentNode.jsletrecno);
        	if((recno || recno === 0) &amp;&amp; colCfg) {
        		var fldName = colCfg.field;
        		if(fldName) {
        			Z._showSelected(otd, fldName, recno);
        		}
        	}
		});
	},
	
	_syncScrollBar: function(rowno) {
		var Z = this;
		if (Z._keep_silence_) {
			return;
		}
		var	sw = rowno * Z.rowHeight();
		Z._keep_silence_ = true;
		try {
			var scrBar = Z.jqVScrollBar[0];
			window.setTimeout(function() {
				scrBar.scrollTop = sw;
			}, 10);
		} finally {
			Z._keep_silence_ = false;
		}
	},

<span id='jslet-ui-AbstractDBTable-method-toggle'>	/**
</span>	 * Toggle(expand/collapse) the current record expanded status, enabled for tree style table.
	 */
	toggle: function() {
		var Z = this;
		if(Z._dataset.recordCount() === 0) {
			return;
		}
		var expanded = Z._dataset.expandedByRecno(Z._dataset.recno());
		if (expanded) {
			Z.listvm.collapse(function() {
				Z._fillData();
			});
		} else {
			Z.listvm.expand(function() {
				Z._fillData();
			});
		}
	},
	
<span id='jslet-ui-AbstractDBTable-method-expand'>	/**
</span>	 * Expand the current record, enabled for tree style table.
	 */
	expand: function() {
		var Z = this;
		if(Z._dataset.recordCount() === 0) {
			return;
		}
		var expanded = Z._dataset.expandedByRecno(Z._dataset.recno());
		if (!expanded) {
			Z.listvm.expand(function() {
				Z._fillData();
			});
		}
	},
	
<span id='jslet-ui-AbstractDBTable-method-collapse'>	/**
</span>	 * Collapse the current record, enabled for tree style table.
	 */
	collapse: function() {
		var Z = this;
		if(Z._dataset.recordCount() === 0) {
			return;
		}
		var expanded = Z._dataset.expandedByRecno(Z._dataset.recno());
		if (expanded) {
			Z.listvm.collapse(function() {
				Z._fillData();
			});
		}
	},
	
<span id='jslet-ui-AbstractDBTable-method-expandAll'>	/**
</span>	 * Expand all records, enabled for tree style table.
	 */
	expandAll: function() {
		var Z = this;
		Z.listvm.expandAll(function() {
			Z._fillData(); 
		});
	},

<span id='jslet-ui-AbstractDBTable-method-collapseAll'>	/**
</span>	 * Collapse all records, enabled for tree style table.
	 */
	collapseAll: function() {
		var Z = this;
		Z.listvm.collapseAll(function() {
			Z._fillData(); 
		});
	},

<span id='jslet-ui-AbstractDBTable-method-_doMetaChanged'>	/**
</span>	 * @override
	 */
	_doMetaChanged: function(metaName, fldName) {
		var Z = this;
		if(!fldName) {
			Z.renderAll();
			return;
		}
		if(metaName == 'label' &amp;&amp; !Z._hideHead) {
			Z._refreshHeadCell(fldName);
			return;
		}
		
		if(metaName == 'required' &amp;&amp; Z._editable &amp;&amp; !Z._hideHead) {
			Z._refreshHeadCell(fldName);
			return;
		}

		if(metaName == 'visible') {
			
		}
		if(Z._editable &amp;&amp; (metaName == 'readOnly' || metaName == 'disabled')) {
			var cellEditor = Z.cellEditor();
			if(cellEditor) {
				var currFld = cellEditor.currentField();
				if(currFld == fldName) {
					cellEditor.showEditor(fldName);
				}
			}
		}
	},
	
	refreshControl: function(evt) {
		var Z = this, i, cnt, otr, otd, checked, ocheckbox, col, recno,
			evtType = evt.eventType;
		if (evtType == jslet.data.RefreshEvent.CHANGEMETA) {
			Z._doMetaChanged(evt.metaName, evt.fieldName);
		} else if (evtType == jslet.data.RefreshEvent.AGGREGATED) {
			Z._fillTotalSection();			
		} else if (evtType == jslet.data.RefreshEvent.BEFORESCROLL) {
			
		} else if (evtType == jslet.data.RefreshEvent.SCROLL) {
			if (Z._dataset.recordCount() === 0) {
				return;
			}
			Z._showCurrentRow(true);
		} else if (evtType == jslet.data.RefreshEvent.UPDATEALL) {
			if(!Z.listvm) {
				return;
			}
			Z._hideCurrentRow();
			Z.listvm.refreshModel();
			Z._stretchContentHeight();
			Z._updateSortFlag(true);
			if(Z._dataset.recordCount() === 0) {
				Z._currRow = null;
			}
			Z._fillData();
			Z._showCurrentRow(true);
			if(Z._filterPanel) {
				Z._filterPanel.checkFilterBtnStyle();
			}
			//Clear &quot;Select all&quot; checkbox
			if(Z._hasSelectCol) {
				jQuery(Z.el).find('.jl-tbl-select-all')[0].checked = false;
			}
		} else if (evtType == jslet.data.RefreshEvent.UPDATERECORD) {
			var fldName = evt.fieldName;
			if(fldName) {
				var fldObj = Z._dataset.getField(fldName);
				if(fldObj &amp;&amp; fldObj.mergeSame()) {
					Z._fillData();
					return;
				}
			}
			Z.refreshCurrentRow();
		} else if (evtType == jslet.data.RefreshEvent.UPDATECOLUMN || evtType == jslet.data.RefreshEvent.UPDATELOOKUP) {
			Z._fillData();
		} else if (evtType == jslet.data.RefreshEvent.INSERT) {
			Z.listvm.refreshModel();
			Z._stretchContentHeight();
			recno = Z._dataset.recno();
			var	preRecno = evt.preRecno;

			Z._fillData();
			Z._keep_silence_ = true;
			try {
				Z.refreshControl(jslet.data.RefreshEvent.scrollEvent(recno, preRecno));
			} finally {
				Z._keep_silence_ = false;
			}
		} else if (evtType == jslet.data.RefreshEvent.DELETE) {
			Z.listvm.refreshModel();
			Z._stretchContentHeight();
			Z._fillData();
			if(Z._dataset.recordCount() === 0) {
				Z._currRow = null;
			}
		} else if (evtType == jslet.data.RefreshEvent.SELECTRECORD) {
			if (!Z._hasSelectCol) {
				return;
			}
			col = 0;
			if (Z._hasSeqCol) {
				col++;
			}
			recno = evt.recno;
			for(i = 0, cnt = recno.length; i &lt; cnt; i++) {
				otr = Z._getLeftRowByRecno(recno[i]);
				if (!otr) {
					continue;
				}
				otd = otr.cells[col];
				checked = evt.selected ? true : false;
				ocheckbox = jQuery(otd).find('[type=checkbox]')[0];
				ocheckbox.checked = checked;
				ocheckbox.defaultChecked = checked;
			}
		} else if (evtType == jslet.data.RefreshEvent.SELECTALL) {
			if (!Z._hasSelectCol) {
				return;
			}
			col = 0;
			if (Z._hasSeqCol) {
				col++;
			}
			var leftFixedBody = Z.leftFixedTbl.tBodies[0],
				leftContentBody = Z.leftContentTbl.tBodies[0],
				rec,
				oldRecno = Z._dataset.recno();

			try {
				for (i = 0, cnt = leftFixedBody.rows.length; i &lt; cnt; i++) {
					otr = leftFixedBody.rows[i];
					if (otr.style.display == 'none') {
						break;
					}
					Z._dataset.recnoSilence(otr.jsletrecno);
					checked = Z._dataset.selected() ? true : false;
					otd = otr.cells[col];
					ocheckbox = jQuery(otd).find('[type=checkbox]')[0];
					ocheckbox.checked = checked;
					ocheckbox.defaultChecked = checked;
				}

				for (i = 0, cnt = leftContentBody.rows.length; i &lt; cnt; i++) {
					otr = leftContentBody.rows[i];
					if (otr.style.display == 'none') {
						break;
					}
					Z._dataset.recnoSilence(otr.jsletrecno);
					checked = Z._dataset.selected() ? true : false;
					otd = otr.cells[col];
					ocheckbox = jQuery(otd).find('[type=checkbox]')[0];
					ocheckbox.checked = checked;
					ocheckbox.defaultChecked = checked;
				}
			} finally {
				Z._dataset.recnoSilence(oldRecno);
			}
		} //end event selectall
	}, // refreshControl

	_isCellEditable: function(colCfg) {
		var Z = this;
		if (!Z._editable) {
			return false;
		}
		var fldName = colCfg.field;
		if (!fldName) {
			return false;
		}
		if(Z._editableFields &amp;&amp; Z._editableFields.length &gt; 0 &amp;&amp; Z._editableFields.indexOf(fldName) &lt; 0) {
			return false;
		}
		if(Z._readOnlyFields &amp;&amp; Z._readOnlyFields.length &gt; 0 &amp;&amp; Z._readOnlyFields.indexOf(fldName) &gt;=0) {
			return false;
		}
		var fldObj = Z._dataset.getField(fldName),
			isEditable = !fldObj.fieldDisabled() &amp;&amp; !fldObj.fieldReadOnly() ? 1 : 0;
		return isEditable;
	},
	
	_doAppendRecord: function() {
		var Z = this;
		if(Z._onAppendRecord) {
			Z._onAppendRecord.call(Z);
		} else {
			Z._dataset.appendRecord();
		}
	},
	
	_doDeleteRecord: function() {
		var Z = this;
		if(Z._onDeleteRecord) {
			Z._onDeleteRecord.call(Z);
		} else {
			Z._dataset.deleteRecord();
		}
	},
	
<span id='jslet-ui-AbstractDBTable-method-checkSizeChanged'>	/**
</span>	 * @protected
	 * 
	 * Run when container size changed, it's revoked by jslet.ui.resizeEventBus.
	 * 
	 */
	checkSizeChanged: function() {
		var Z = this,
			jqEl = jQuery(Z.el),
			newHeight = jqEl.height();
		if (newHeight == Z._oldHeight) {
			return;
		}
		Z.height = newHeight;
		Z.renderAll();
	},
	
	_innerDestroy: function() {
		var Z = this, 
			jqEl = jQuery(Z.el);
		Z._currRow = null;
		Z._oldVisibleRows = -1;
		Z.listvm.reset();
		Z.leftHeadTbl = null;
		Z.rightHeadTbl = null;
		jQuery(Z.rightHeadTbl).off();

		Z.leftFixedTbl = null;
		Z.rightFixedTbl = null;

		Z.leftContentTbl = null;
		Z.rightContentTbl = null;

		Z.leftFootTbl = null;
		Z.rightFootTbl = null;
		
		Z.noRecordDiv = null;
		if(Z.jqVScrollBar) {
			Z.jqVScrollBar.off();
		}
		Z.jqVScrollBar = null;
		
		var splitter = jqEl.find('.jl-tbl-splitter')[0];
		if(splitter) {
			splitter._doDragging = null;
			splitter._doDragEnd = null;
			splitter._doDragCancel = null;
		}
		Z.parsedHeads = null;
		Z.prevCell = null;
		
		jqEl.find('.jl-tbl-select-check').off();
		if(Z._filterPanel) {
			Z._filterPanel.destroy();
			Z._filterPanel = null;
		}
		
		if(Z._findDialog) {
			Z._findDialog.destroy();
			Z._findDialog = null;
		}
		
		if(Z._cellEditor) {
			Z._cellEditor.destroy();
			Z._cellEditor = null;
		}		
	},
	
<span id='jslet-ui-AbstractDBTable-method-destroy'>	/**
</span>	 * @override
	 */
	destroy: function($super) {
		var Z = this, 
			jqEl = jQuery(Z.el);
		jslet.ui.resizeEventBus.unsubscribe(Z);
		jqEl.off();
		Z._innerDestroy();
		Z.listvm.destroy();
		Z.listvm = null;
		$super();
	} 
});

<span id='jslet-ui-DBTable'>/**
</span> * @class
 * @extend jslet.ui.AbstractDBTable
 * 
 * DBTable is a powerful control, features: &lt;br /&gt;
 * &lt;ol&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Sort Data&lt;/dt&gt;&lt;dd&gt;Clicking column header text to sort data, SHIFT + Clicking column header text to sort data with multiple columns.&lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Find Data&lt;/dt&gt;&lt;dd&gt;Pressing CTRL + F to pop up finding dialog.&lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Filter Data&lt;/dt&gt;&lt;dd&gt;Pressing the down-arrow of each column header to pop up filtering dialog.&lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Copy Data to Clipboard&lt;/dt&gt;&lt;dd&gt;First select cells(CTRL + A, CTRL/SHIFT + Clicking Cell), pressing CTRL + C to copy data.&lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Find Error Record&lt;/dt&gt;&lt;dd&gt;When importing records or batch editing records, press CTRL + E to find the next record existing error.&lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Multi-Layer Column Header&lt;/dt&gt;&lt;dd&gt;Set dataset field's property &quot;children&quot;.&lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Change Column Width&lt;/dt&gt;&lt;dd&gt;Drag and drop column line.&lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Move Column&lt;/dt&gt;&lt;dd&gt;Drag and drop column header.&lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Support Sequence Column&lt;/dt&gt;&lt;dd&gt;Set property &quot;hasSeqCol&quot; to true to show sequence column; &lt;br /&gt;
 * Set property &quot;reverseSeqCol&quot; to &quot;true&quot; to reverse sequence no; &lt;br /&gt;
 * Set property &quot;seqColHeader&quot; to customize sequence header. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Support Select Column&lt;/dt&gt;
 * &lt;dd&gt;Set property &quot;hasSelectCol&quot; to &quot;true&quot; to show select column.&lt;br /&gt;
 * Clicking &quot;Select Column&quot; header to select all records.&lt;br /&gt;
 * Set property &quot;selectBy&quot; to group select records.&lt;br /&gt;
 * Set dataset event &quot;onCheckSelectable&quot; to check which records can be selected. 
 * &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Support Action Column&lt;/dt&gt;&lt;dd&gt;Add a &quot;ACTION&quot; type field to dataset. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Support Edit Action Column&lt;/dt&gt;&lt;dd&gt;Add a &quot;EDITACTION&quot; type field to dataset. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Support Fixed rows or columns&lt;/dt&gt;&lt;dd&gt;Set value to properties &quot;fixedRows&quot; or &quot;fixedCols&quot;. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Support &quot;Total&quot; row&lt;/dt&gt;&lt;dd&gt;Set the dataset field's properties: &quot;aggregated&quot; and &quot;aggregatedBy&quot;. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Support Merge Rows with Same Value&lt;/dt&gt;&lt;dd&gt;Set the dataset field's properties: &quot;mergeSame&quot; and &quot;mergeSameBy&quot;. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Tree-Grid Style&lt;/dt&gt;&lt;dd&gt;Set property: &quot;treeField&quot;. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;In-cell Edit&lt;/dt&gt;&lt;dd&gt;Set property: &quot;editable&quot; to true. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Customize Table Style&lt;/dt&gt;&lt;dd&gt;Use: &quot;noborder&quot;, &quot;onFillRow&quot;, &quot;onFillCell&quot;, &quot;hideHead&quot; to customize. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;li&gt;
 * &lt;dl&gt;&lt;dt&gt;Stretch Table Height Automatically&lt;/dt&gt;&lt;dd&gt;Use: &quot;autoStretch&quot;, &quot;minVisibleRows&quot;, &quot;onFillCell&quot;, &quot;maxVisibleRows&quot; to customize. &lt;/dd&gt;&lt;/dl&gt;
 * &lt;/li&gt;
 * &lt;/ol&gt;
 * 
 * Example:
 * 
 *     @example
 *     var jsletParam = {type:&quot;DBTable&quot;, dataset: &quot;employee&quot;};
 * 
 *     //1. Declaring:
 *     &lt;div id=&quot;chartId&quot; data-jslet='type:&quot;DBTable&quot;, dataset: &quot;employee&quot;' /&gt;
 *     or
 *     &lt;div data-jslet='jsletParam' /&gt;
 *
 *     //2. Binding
 *     &lt;div id=&quot;ctrlId&quot;  /&gt;
 *     //Js snippet
 *     var el = document.getElementById('ctrlId');
 *     jslet.ui.bindControl(el, jsletParam);
 *
 *     //3. Create dynamically
 *     jslet.ui.createControl(jsletParam, document.body);
 */
jslet.ui.DBTable = jslet.Class.create(jslet.ui.AbstractDBTable, {});

jslet.ui.register('DBTable', jslet.ui.DBTable);
jslet.ui.DBTable.htmlTemplate = '&lt;div&gt;&lt;/div&gt;';

/*
* Splitter: used in jslet.ui.DBTable
*/
jslet.ui.Splitter = function() {
	if (!jslet.ui._splitDiv) {
		var odiv = document.createElement('div');
		odiv.className = 'jl-split-column';
		odiv.style.display = 'none';
		jslet.ui._splitDiv = odiv;
		document.body.appendChild(odiv);
		odiv = null;
	}
	
	this.isDragging = false;
	
	this.attach = function(el, left, top, height) {
		if (!height) {
			height = jQuery(el).height();
		}
		var odiv = jslet.ui._splitDiv;
		odiv.style.height = height + 'px';
		odiv.style.left = left + 'px';
		odiv.style.top = top + 'px';
		odiv.style.display = 'block';
		jslet.ui.dnd.bindControl(this);
		this.isDragging = false;
	};
	
	this.unattach = function() {
		jslet.ui._splitDiv.style.display = 'none';
		this.onSplitEnd = null;
		this.onSplitCancel = null;
	};
	
	this.onSplitEnd = null;
	this.onSplitCancel = null;
	
	this._doDragEnd = function(oldX, oldY, x, y, deltaX, deltaY) {
		jslet.ui.dnd.unbindControl();
		if (this.onSplitEnd) {
			this.onSplitEnd(x - oldX);
		}
		this.unattach();
		this.isDragging = false;
	};
	
	this._doDragging = function(oldX, oldY, x, y, deltaX, deltaY) {
		this.isDragging = true;
		jslet.ui._splitDiv.style.left = x + 'px';
	};
	
	this._doDragCancel = function() {
		jslet.ui.dnd.unbindControl();
		if (this.onSplitCancel) {
			this.onSplitCancel();
		}
		this.unattach();
		this.isDragging = false;
	};
};
</pre>
</body>
</html>
